在函数名后面加一个<T>,表示泛型函数,其中T可以是任意的,并且可以支持多个类型参数,命名在尖括号中,用逗号分开。
func swapTwoValues<T>(inout a: T, inout b: T) { let temporaryA = a a = b b = temporaryA }
var someInt = 3 var anotherInt = 107 swapTwoValues(&someInt, &anotherInt) // someInt 107, and anotherInt 3
下图展示了一个栈的压栈(push)/出栈(pop)的行为:
这里展示了如何写一个非泛型版本的栈,Int
值型的栈:
struct IntStack { var items = [Int]() mutating func push(item: Int) { items.append(item) } mutating func pop() -> Int { return items.removeLast() } }
泛型版本:
struct Stack<T> { var items = [T]() mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } }
在一个类型参数名后面的类型约束,通过冒号分割
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { // function body goes here }
上面那个方法要求T是SomeClass类型或是继承自SomeClass。U是SomeProtocol协议,或是实现了SomeProtocol协议
再看一个示例:
func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? { for (index, value) in enumerate(array) { if value == valueToFind { return index } } return nil }
T 表示一个遵循了Equatable的
协议的类型
关联类型使用typealias
关键字。
protocol Container { typealias ItemType // 关联类型 mutating func append(item: ItemType) var count: Int { get } subscript(i: Int) -> ItemType { get } }
Container
协议定义了三个任何容器必须支持的兼容要求:
append
方法添加一个新item到容器里;count
属性获取容器里items的数量,并返回一个Int
值;Int
索引值下标可以检索到每一个item。一个非泛型的版本,遵循Container协议
struct IntStack: Container { // IntStack的原始实现 var items = [Int]() mutating func push(item: Int) { items.append(item) } mutating func pop() -> Int { return items.removeLast() } // 遵循Container协议的实现 typealias ItemType = Int mutating func append(item: Int) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> Int { return items[i] } }
生成遵循Container
协议的泛型Stack
类型:
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] } }
实现一个空扩展的行为:
extension Array: Container {}
定义了这个扩展后,你可以将任何Array
当作Container
来使用。
Where
语句类型约束能够确保类型符合泛型函数或类的定义约束。
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 }
这个函数的类型参数列紧随在两个类型参数需求的后面:
C1
必须遵循Container
协议 (写作 C1: Container
)。C2
必须遵循Container
协议 (写作 C2: Container
)。C1
的ItemType
同样是C2的ItemType
(写作 C1.ItemType == C2.ItemType
)。C1
的ItemType
必须遵循Equatable
协议 (写作 C1.ItemType: Equatable
)。第三个和第四个要求被定义为一个where
语句的一部分,写在关键字where
后面,作为函数类型参数链的一部分。
这些要求意思是:
someContainer
是一个C1
类型的容器。 anotherContainer
是一个C2
类型的容器。someContainer
和anotherContainer
包含相同的元素类型。 someContainer
中的元素可以通过不等于操作(!=
)来检查它们是否彼此不同。
下面演示allItemsMatch函数运算的过程:
// 1、创建一个Stack
单例来存储String
var stackOfStrings = Stack<String>()
// 2、压了三个字符串进栈 stackOfStrings.push("uno") stackOfStrings.push("dos") stackOfStrings.push("tres")
// 3、创建了一个Array
单例,并初始化包含三个同栈里一样的原始字符串 var arrayOfStrings = ["uno", "dos", "tres"]
// 4、调用allItemsMatch
函数,用定义的两个Container作为它的参数 if allItemsMatch(stackOfStrings, arrayOfStrings) { println("All items match.") } else { println("Not all items match.") } // 最后输出 "All items match."
2015-03-25
20:18:09