2015-06-23 4 views
9

Я прошел через обучающие программы Swift на веб-сайте разработчика Apple, но я не понимаю понятия Generics. Кто-нибудь может объяснить это простым способом? Например:Generics in Swift 2.0

func swapTwoValues<T>(inout a: T, inout b: T) { 
    let temporaryA = a 
    a = b 
    b = temporaryA 
} 
+0

Отличный вопрос, вы должны рассмотреть возможность выбора ответа на этот вопрос, если вы удовлетворите свой вопрос! Это улучшает качество вашего сообщения и, таким образом, помогает сообществу. –

ответ

11

Без использования дженериков в примере вы дали, вы должны перегрузить swapTwoValues для каждого типа вы хотели менять местами. Например:

func swapTwoValues(inout a: Int, inout b: Int) { 
    let temp = a 
    a = b 
    b = temp 
} 

func swapTwoValues(inout a: String, inout b: String) { 
    let temp = a 
    a = b 
    b = temp 
} 

// Many more swapTwoValues functions... 

Единственное, что отличается от вышеперечисленных функций, это тип, который они принимают; код внутри каждого точно такой же. Поэтому лучше написать одну общую функцию, которая может принимать любой тип.

Важно отметить, что вы не можете заменить T на Any. Не было бы никакой гарантии, что a и b были бы того же типа - вы не могли бы обменять Int и String, например.

1

По существу, это просто означает, что оно не относится к конкретному типу. Вы можете использовать T и просто написать одну функцию, вместо того, чтобы писать много функций для каждого типа Int, Double, Float, String и т.д.

4

В вашем примере T представляет собой тип. И как только этот тип будет согласован для всей функции.

func swapTwoValues<T>(inout a: T, inout b: T) { 
    let temporaryA = a 
    a = b 
    b = temporaryA 
} 

Если T является Int в случае параметра а, то он также должен быть Int в случае параметра Ь. Как показано в этом использовании функции:

var valueA = 2 
var valueB = 4 
swapTwoValues(&valueA, b: &valueB) 

valueA // 4 
valueB // 2 

Мы не могли поменять местами строки для Int, например, или даже Int для двойной, но до тех пор, как типы одинаковы, то этот общий метод будет принимать какие-либо Тип, потому что он безудержный во всех других отношениях.

var valueA = "Hello" 
var valueB = "Swift" 
swapTwoValues(&valueA, b: &valueB) 

valueA // "Swift" 
valueB // "Hello" 

Это, однако, не означает, что многие функции исключены из общих функций. Вам просто нужно назначить другую букву для представления разных типов (используемая буква не имеет значения, T просто используется, потому что она является первой буквой типа, но нет причин, по которой она не может быть заменена Q, например, или любым другое письмо):

func swapTwoValues<T,S>(inout a: T, inout b: T, inout c: S, inout d: S) { 
    let temporaryA = a 
    a = b 
    b = temporaryA 

    let temporaryC = c 
    c = d 
    d = temporaryC 
} 

var valueA = 2 
var valueB = 4 

var valueC = "Hello" 
var valueD = "Swift" 
swapTwoValues(&valueA, b: &valueB, c:&valueC, d:&valueD) 

valueA // 4 
valueB // 2 

valueC // "Swift" 
valueD // "Hello" 

Примечание: мы не можем обменять T для S, потому что Swift является строго типизированным языком, и у нас нет никакой уверенности, они одинаковы.

Это становится более интересным, когда протоколы участвуют, чтобы ограничить общие типы. Здесь я делаю это с UnsignedIntegerType:

func swapTwoValues<T: UnsignedIntegerType>(inout a: T, inout b: T) { 
    let temporaryA = a 
    a = b 
    b = temporaryA 
} 

var valueA:UInt = 10 
var valueB:UInt = 11 

swapTwoValues(&valueA, b: &valueB) 

Теперь только такие типы, как UInt, Uint8, UInt32, и т.д., являются приемлемыми, а все остальные значения будут отклонены и создать ошибку.

Примечание. Причина ограничения типов с использованием протоколов заключается в том, что определенные методы могут быть гарантированы. Например, если для создания экземпляра нового типа требуется общая функция, тогда она должна принять протокол с методом init. (Вы можете проверить принятие протокола для каждого типа в документах Apple.)

Мы можем пойти дальше и использовать where ключевое слово для определения типов, содержащихся в общей коллекции:

func swapTwoValues<T: CollectionType where T.Generator.Element: UnsignedIntegerType>(inout a: T, inout b: T) { 
    let temporaryA = a 
    a = b 
    b = temporaryA 
} 

var valueA:[UInt] = [10,12,4] 
var valueB:[UInt] = [11,45,67] 

swapTwoValues(&valueA, b: &valueB) 

valueA // [11, 45, 67] 
valueB // [10, 12, 4] 

Или сделать что-то вроде проверки, что второй тип эквивалентен типу элементов в коллекции используя ==:

func swapTwoValues<T: CollectionType, S where S == T.Generator.Element>(inout a: T, inout b: T, inout c: S, inout d: S) { 
    let temporaryA = a 
    a = b 
    b = temporaryA 

    let temporaryC = c 
    c = d 
    d = temporaryC 
} 

Дальнейшее чтение: Вещи становятся еще более интересными с protocol extensions in Swift 2, потому что теперь общие функции могут взять на себя характеристики методов типа, что делает их далеко м руды можно обнаружить.