类型参数
占位类型T
是类型参数的一个例子。类型参数指定并命名一个占位类型,并且紧随在函数名后面,使用一对尖括号括起来<T>
。
一旦一个类型参数被指定,你可以用它来定义一个函数的参数类型,或者作为函数的返回类型,还可以用作函数主体中的注释类型。在这些情况下,类型参数会在函数调用时被实际类型所替换。
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
在下面的两个例子中,T
分别代表Int
和String
:
var a = 10
var b = 20
swap(&a, &b)
print("a: \(a), b: \(b)") // a: 20, b: 10
var x = "hello"
var y = "world"
swap(&x, &y)
print("x: \(x), y: \(y)") // x: world, y: hello
命名类型参数
在大多数情况下,类型参数具有一个描述性名字,例如 Dictionary<Key, Value>
中的Key
和Value
,以及 Array<Element>
中的Element
,这可以告诉阅读代码的人这些类型参数和泛型函数之间的关系。然而,当它们之间没有有意义的关系时,通常使用单个字母来命名,例如T
、U
、V
。
泛型类型
除了泛型函数,Swift
还允许你定义泛型类型。这些自定义类、结构体和枚举可以适用于任何类型。
struct Stack<Element> {
var items = [Element]()
}
扩展一个泛型类型
当你扩展一个泛型类型的时候,你并不需要在扩展的定义中提供类型参数列表。原始类型定义中声明的类型参数列表在扩展中可以直接使用,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。
extension Stack {
var firstItem: Element? {
return items.isEmpty ? nil : items[0]
}
}
类型约束
有的时候如果能将使用在泛型函数和泛型类型中的类型添加一个特定的类型约束,将会是非常有用的。类型约束可以指定一个类型参数必须继承自指定类,或者符合一个特定的协议或协议组合。
你可以在一个类型参数名后面放置一个类名或者协议名,并用冒号进行分隔,来定义类型约束,它们将成为类型参数列表的一部分。对泛型函数添加类型约束的基本语法如下所示(作用于泛型类型时的语法与之相同):
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// 这里是泛型函数的函数体部分
}
关联类型
定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分将会非常有用。关联类型为协议中的某个类型提供了一个占位名(或者说别名),其代表的实际类型在协议被采纳时才会被指定。你可以通过associatedtype
关键字来指定关联类型。
protocol Container {
associatedtype ItemType
mutating func append(_ item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
Stack
结构体遵从Container
协议:
struct Stack<Element>: Container {
var items = [Element]()
// Container 协议实现
typealias ItemType = Element
var count: Int {
return items.count
}
mutating func append(_ item: Element ) {
items.append(item)
}
subscript(i: Int) -> Element {
return items[i]
}
}
实例
var stack = Stack(items: [2, 3, 5])
stack.append(7)
for i in 0..<stack.count {
print(stack[i])
}
// a
// b
// c
// d
泛型 Where 语句
为关联类型定义约束也是非常有用的。你可以在参数列表中通过where
子句为关联类型定义约束。你能通过where
子句要求一个关联类型遵从某个特定的协议,以及某个特定的类型参数和关联类型必须类型相同。你可以通过将where
关键字紧跟在类型参数列表后面来定义where
子句,where
子句后跟一个或者多个针对关联类型的约束,以及一个或多个类型参数和关联类型间的相等关系。你可以在函数体或者类型的大括号之前添加where
子句。
func allItemsMatch<C1: Container, C2: Container> (_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
// 检查两个容器含有相同数量的元素
if someContainer.count != anotherContainer.count {
return false
}
// 检查每一对元素是否相等
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
// 所有元素都匹配,返回 true
return true
}
具有泛型 where 子句的扩展
你也可以使用泛型where
子句作为扩展的一部分。
extension Stack where Element: Equatable {
func isFirst(_ item: Element) -> Bool {
guard let firstItem = items.first else {
return false
}
return firstItem == item
}
}