Swift generics


Swift provides generics that allow you to write flexible and reusable functions and types.

The Swift standard library is built with generic code.

Swift’s array and dictionary types are both generic sets.

You can create an Int array, a String array, or even any other Swift type data array.

The following example is a non-generic function exchange used to exchange two Int values:

// 定义一个交换两个变量的函数
func exchange(inout a: Int, inout b: Int) {
    let temp = a
    a = b
    b = temp
}

var numb1 = 100
var numb2 = 200

print("交换前数据: \(numb1) 和 \(numb2)")
exchange(&numb1, b: &numb2)
print("交换后数据: \(numb1) 和 \(numb2)")

The execution output of the above program is:

交换前数据: 100 和 200
交换后数据: 200 和 100

Generic functions can access any Type, such as Int or String.

The following example is a generic function exchange used to exchange two Int and String values:

func exchange<T>(inout a: T, inout b: T) {
    let temp = a
    a = b
    b = temp
}

var numb1 = 100
var numb2 = 200

print("交换前数据:  \(numb1) 和 \(numb2)")
exchange(&numb1, b: &numb2)
print("交换后数据: \(numb1) 和 \(numb2)")

var str1 = "A"
var str2 = "B"

print("交换前数据:  \(str1) 和 \(str2)")
exchange(&str1, b: &str2)
print("交换后数据: \(str1) 和 \(str2)")

The output result of the execution of the above program is:

交换前数据:  100 和 200
交换后数据: 200 和 100
交换前数据:  A 和 B
交换后数据: B 和 A

The generic type of this function The version uses a placeholder type name (usually represented by the letter T in this case) instead of the actual type name (such as Int, String, or Double). The placeholder type name does not hint what type T must be, but it hints that a and b must be the same type T, regardless of what type T represents. Only the actual type passed in each time the exchange(_:_:) function is called can determine the type represented by T.

Another difference is that the placeholder type name (T) following the generic function name is enclosed in angle brackets (


Generic type

Swift allows you to define your own generic types.

Custom classes, structures and enumerations work with any type, just like the usage of Array and Dictionary.

struct TOS<T> {
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    
    mutating func pop() -> T {
        return items.removeLast()
    }
}

var tos = TOS<String>()
tos.push("Swift")
print(tos.items)

tos.push("泛型")
print(tos.items)

tos.push("类型参数")
print(tos.items)

tos.push("类型参数名")
print(tos.items)


let deletetos = tos.pop()

The above program. The execution output is:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "类型参数"]
["Swift", "泛型", "类型参数", "类型参数名"]

Extended generic type

When you extend a generic type (using the extension keyword), you do not need to include it in the extension definition Provide a type parameter list. More conveniently, the type parameter list declared in the original type definition is available in the extension, and the parameter names from the original type will be used as references to the type parameters in the original definition ##.

#Example

The following example extends the generic TOS type and adds a read-only computed property named first, which will return the element at the top of the current stack without moving it from the stack. Except.

##

struct TOS<T> {
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    
    mutating func pop() -> T {
        return items.removeLast()
    }
}

var tos = TOS<String>()
tos.push("Swift")
print(tos.items)

tos.push("泛型")
    print(tos.items)
    
    tos.push("类型参数")
    print(tos.items)
    
    tos.push("类型参数名")
    print(tos.items)

// 扩展泛型 TOS 类型
extension TOS {
    var first: T? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}

if let first = tos.first {
    print("栈顶部项:\(first)")
}

The output result of the above program execution is:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "类型参数"]
["Swift", "泛型", "类型参数", "类型参数名"]
栈顶部项:类型参数名

Type constraint

The type constraint specifies a class that must be inherited from the specified class. Type parameters, or follow a specific protocol or protocol composition

Type constraint syntax

You can write a type constraint after a type parameter name, separated by a colon, as a type. Part of the parameter chain. The basic syntax of this type constraint acting on a generic function is as follows (the same as the syntax of a generic type):

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 这里是函数主体
}

Example

// 函数可以作用于查找一字符串数组中的某个字符串
func findStringIndex(array: [String], _ valueToFind: String) -> Int? {
    for (index, value) in array.enumerate() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
if let foundIndex = findStringIndex(strings, "llama") {
    print("llama 的下标索引值为 \(foundIndex)")
}

The above program execution output The result is:

llama 的下标索引值为 2

Associated type instance

The typealias keyword is used in Swift to set the associated type.

When defining a protocol, sometimes a or is declared. Multiple associated types are very useful as part of the protocol definition.

protocol Container {
    // 定义了一个ItemType关联类型
    typealias ItemType
    mutating func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

// 遵循Container协议的泛型TOS类型
struct TOS<T>: Container {
    // original Stack<T> implementation
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    
    mutating func pop() -> T {
        return items.removeLast()
    }
    
    // conformance to the Container protocol
    mutating func append(item: T) {
        self.push(item)
    }
    
    var count: Int {
        return items.count
    }
    
    subscript(i: Int) -> T {
        return items[i]
    }
}

var tos = TOS<String>()
tos.push("Swift")
print(tos.items)

tos.push("泛型")
print(tos.items)

tos.push("参数类型")
print(tos.items)

tos.push("类型参数名")
print(tos.items)

The output of the above program execution is:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "参数类型"]
["Swift", "泛型", "参数类型", "类型参数名"]

Where statement

Type constraints can ensure The type conforms to the definition constraints of the generic function or class

.

You can define parameter constraints through where statements in the parameter list.

You can write a where statement immediately after the type parameter list. The where statement is followed by one or more constraints on the associated type, and/or one or more constraints between the type and the associated type. Equality relationship.

Example

The following example defines a generic function named allItemsMatch to check whether two Container instances contain the same elements in the same order.

If all elements can match, then return a Boolean value of true, otherwise false.

protocol Container {
    typealias ItemType
    mutating func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

struct Stack<T>: Container {
    // original Stack<T> implementation
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    
    mutating func pop() -> T {
        return items.removeLast()
    }
    
    // conformance to the Container protocol
    mutating func append(item: T) {
        self.push(item)
    }
    
    var count: Int {
        return items.count
    }
    
    subscript(i: Int) -> T {
        return items[i]
    }
}

func allItemsMatch<
    C1: Container, C2: Container
    where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
    (someContainer: C1, anotherContainer: C2) -> Bool {
        // 检查两个Container的元素个数是否相同
        if someContainer.count != anotherContainer.count {
            return false
        }
        
        // 检查两个Container相应位置的元素彼此是否相等
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }
        // 匹配所有项,返回 true
        return true
}

var tos = Stack<String>()
tos.push("Swift")
print(tos.items)

tos.push("泛型")
print(tos.items)

tos.push("Where 语句")
print(tos.items)

var eos = ["Swift", "泛型", "Where 语句"]
print(eos)

The output result of the execution of the above program is:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "Where 语句"]
["Swift", "泛型", "Where 语句"]