泛型代码让你能够自定义要求,编写出适用于任意类型、灵活可重用的函数及类型。它能够让你避免代码重复,用一种清晰抽象的方式来表达意图。
泛型函数
1 | func swapTwoValues<T>(_ a: inout T, _ b: inout T){ |
类型参数
你可以提供多个类型参数,将他们都写在尖括号中,用逗号隔开
命名类型参数
大多数情况下,命名参数具有一个描述性名字,例如 Dictionary<Key, Value> 中的Key和Value,以及Array
泛型类型
除了泛型函数,Swift还允许你定义泛型类型.
1 | struct Stack<Element> { |
Element 为待提供的类型定义了一个占位名。
1 | var stackOfString = Stack<String>() |
扩展一个泛型类型
1 | extension Stack { |
类型约束
类型约束是指定一个类型参数必须继承指定类,或者符合一个特定的协议或协议组合。例如,Swift中的Dictionary类型对字典的键类型做了限制。字典的键必须是可哈希(hashable)的。
类型约束语法
1 | func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U){ |
类型约束实践
1 | func findIndex<T>(of valueToFind: T, in array:[T]) -> Int? { |
上面所写的函数无法通过编译。问题出在相等性检查上。不是所有的Swift类型都可以用等式符进行比较。Swift标准库中定义了一个Equatalbe协议。该协议要求任何遵循的类型必须实现等式符(==)及不等符(!=),从而能对改类型的任意两个值进行比较
1 | func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int ? { |
关联类型
关联类型为协议中的某个类型提供一个占位名,其代表的实际类型在协议被采纳时才会被指定。你可以通过 associatedtype关键字来指定关联类型。
实践
1 | //定义一个Container 协议,该协议定义了一个关联类型Item |
定义一个非泛型的IntStack类型。采纳并符合了Container协议
1 | struct IntStack: Container { |
指定Item为Int类型,即typealias Item = Int,从而将Container协议中抽象的Item类型转换为具体的Int类型
下面定义泛型Stack结构体并且遵循Container协议
1 | struct Stack<Element>: Container{ |
通过扩展一个已经存在的类型来指定关联类型
Swift的Array类型已经提供了Container协议所有的属于、方法、以及下标的实现。这意味着你只需要简单的申明Array采纳协议就可以扩展Array
1 | extension Array: Container {} |
定义了这个扩展之后,你可以将任意的Array当做Container
给关联类型添加约束
1 | protocol Container { |
为了遵循Container协议,Item类型也必须遵循Equatable协议
在关联类型约束里使用协议
协议可以作为自身的要求出现
1 | protocol SuffixableContainer : Container { |
这个协议中的Suffix是个关联类型。它有2个约束。它必须遵循SuffixableContainer协议(就是当前定义的协议),以及它的Item类型必须和容器里的Item类型相同
1 | extension Stack: SuffixableContainer { |
泛型Where语句
类型约束让你能够为泛型函数,下标,类型的类型参数定义一些强制要求。where子句跟一个或多个针对关联类型的约束,以及一个或多个类型参数和关联类型间的相等关系
1 | func allItemsMatch<C1: Container, C2: Container>(_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.Item == C2.Item, C1.Item: Equatable { |
函数参数列表的两个类型的要求
- C1必须符合 Container 协议
- C2必须符合 Container 协议
- C1的Item和C2的Item类型相同
- C1的Item必须符合Equatable协议
具有泛型Where子句的扩展
1 | extension Stack where Element: Equatalbe { |
//如果尝试在其元素不符合Equatable协议栈上调用isTop(_:)方法,则会编译报错
使用where字句去扩展一个协议
where子句要求Item符合协议
1 | extension Container where Item: Equatable { |
where字句要求Item为特定的类型
1 | extension Container where Item == Double { |
具有泛型Where字句的关联类型
你可以在关联类型后面加上具有泛型where的子句
1 | protocol Container { |
迭代器(Iterator)的泛型where字句要求,无论迭代器是什么类型,迭代器中的元素必须和容器项目的类型保持一致
泛型下标
下标是能够泛型的,他们能够包含泛型where子句
1 | extension Container { |