23. 泛型

文章目录
  1. 1. 泛型解决的问题
  2. 2. 泛型函数
  3. 3. 类型参数
  4. 4. 泛型类型
  5. 5. 扩展泛型类型
  6. 6. 类型约束
    1. 6.1. 为啥需要类型约束?
  7. 7. 关联类型
    1. 7.1. 将约束添加到关联类型
  8. 8. 泛型 Where ⼦句
    1. 8.1. 在扩展中使⽤泛型Where⼦句
  9. 9. 泛型下标

官方目录:

  1. The Problem That Generics Solve
  2. Generic Functions
  3. Type Parameters
  4. Naming Type Parameters
  5. Generic Types
  6. Extending a Generic Type
  7. Type Constraints
  8. Associated Types
  9. Generic Where Clauses
  10. Extensions with a Generic Where Clause
  11. Associated Types with a Generic Where Clause
  12. Generic Subscripts

泛型解决的问题

泛型解决某些功能,例如交换两个数,由于参数类型不同,需要重复编写代码的问题。

泛型函数

1
2
3
4
5
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a=b
b = temporaryA
}

泛型版本的函数使用占位符类型名,而不是一个真正的类型名称。调用泛型函数的时候,从实参中推断类型T。

类型参数

类型参数指定并命名一个占位类型,并紧挨着函数名称后面,使用一对尖括号括起来(例如 )。类型参数在调用时被一个真实的类型所替换。你可以通过在尖括号内写多个类型参数名来提供多个类型参数,用逗号隔开。

类型参数:将类型作为参数

泛型类型

除了泛型函数外,Swift可以定义你自己的泛型类型 。这些自定义的类、结构体、枚举可以和任何类型一起使用,方式类似于数组、字典 。

1
2
3
4
5
6
7
8
9
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}

扩展泛型类型

扩展泛型类型时,你不需要提供类型参数列表作为扩展定义的一部分。 相反,原始类型定义中的类型参数列表在扩展的主体内依旧可用,并且原始类型参数名称会被用于引用原始定义中的类型参数。

类型约束

有时候,对泛型函数和泛型类型进行类型约束是很有用的。类型约束指定参数类型必须继承自特定的类、遵循特定的协议、特定的协议组。

###类型约束语法

类型约束的写法:在类型参数名后跟上一个类或协议来进行约束,使用冒号进行分割,作为类型参数列表的一部分。泛型函数的类型约束语法如下(泛型类型的语法与此相同)

1
2
3
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// 这⾥里里写函数体的内容
}

第一个类型参数T,类型约束要求T的类型必须是SomeClass的子类。第二个类型参数U,类型约束要求U必须遵循SomeProtocol协议

为啥需要类型约束?

想想字典的key,必须可哈希,不是所有的类型都能作为字典的key,所以需要约束。

关联类型

当定义一个协议时,有时候定义一个或多个关联类型作为协议的一部分是很有用的。关联类型作为协议的一部分,并为一种类型提供占位符名称。在实现该协议之前不会指定关联类型的实际类型。关联类型使用 associatedtype 关键字来指定。

1
2
3
4
5
6
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}

item 可以通过类型推断得到。或者显式的指定:

1
typealias Item = Int

将约束添加到关联类型

1
2
3
4
5
6
protocol Container {
associatedtype Item: Equatable
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}

为了遵循这个版本的 Container ,容器的 Item 类型必须符合 Equatable 协议。

泛型 Where ⼦句

对关联类型定义一些规定通常也很有用。你可以通过定义泛型where子句 来完成此操作。泛型 where 子句使你能够要求关联类型必须符合某个协议,或者某些类型参数和相关类型必须相同。泛型 where子句以where 关键字开头,后跟关联类型的约束条件或类型和关联类型之间的相等关系。

where的位置:你需要在一个类型或函数体的起始大括号之前写一个泛型where子句。

在扩展中使⽤泛型Where⼦句

1
associatedtype Iterator: IteratorProtocol where Iterator.Element == Item

泛型下标

标也可以用泛型表示,同时也可以包含泛型 where 子句。 可以在下标括号之后的尖括号内写一个类型占位符,在下标主体的起始大括号之前写一个泛型 where子句。