Swift学习41:泛型

张建 lol

泛型

  • 泛型代码能根据所定义的要求写出可以用于任何类型的灵活的、可复用的函数。可以编写出 可复用、意图表达清晰、抽象的代码

  • 泛型 是Swift最强大的特性之一,很多Swift标准库 是基于 泛型 代码构建的。如,Swift 的 Array和Dictionary类型都是泛型集合;你也可以创建一个容纳 Int 值的数组,或者容纳 String 值的数组,甚至容纳任何 Swift 可以创建的其他类型的数组。同样,可以创建一个存储任何指定类型值的字典,而且类型没有限制。

  • 泛型所解决的问题:代码的复用性和抽象能力。比如,交换两个值,这里的值可以是 Int、Double、String

1
2
3
4
5
6
// 经典例子swap,使用泛型,可以满足不同类型参数的调用
func swap<T>(_ a: inout T, _ b: inout T){
let tmp = a
a = b
b = tmp
}

基础语法

主要讲3点:类型约束、关联类型、Where语句

  • 类型约束

在一个 类型参数后面放置协议或者是类,例如下面的例子,要求 类型参数T 遵循 Equatable 协议。

1
2
3
4
5
6
7
func test<T:Equatable>(_ a: T, _ b: T) -> Bool {
return a == b
}
test(1, 2)
// 打印结果 false
test("A", "a")
// 打印结果 false

Equatable协议:可以比较值相等的协议,即可以使用 == 比较

  • 关联类型

在定义协议时,使用 关联类型协议 中用到的 类型 起一个 占位符名称。关联类型 只能用于协议,并且是通过关键字 associatedtype 指定。

下面这个示例,仿写的一个栈的结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct Stack {
var items = [Int]()
// 入栈
mutating func push(_ item: Int) {
items.append(item)
}
// 出栈
mutating func pop() -> Int?{
if items.isEmpty {
return nil
}
return items.removeLast()
}
}

该结构体中有个成员 item,是个只能存储 Int 类型的数组,如果想使用其他类型呢? 可以通过协议来实现 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protocol StackProtocol {
// 协议中使用类型的占位符
associatedtype Item
}

struct Stack: StackProtocol{
// 在使用时,需要指定具体的类型
typealias Item = Int

var items = [Item]()
// 入栈
mutating func push(_ item: Int) {
items.append(item)
}
// 出栈
mutating func pop() -> Int?{
if items.isEmpty {
return nil
}
return items.removeLast()
}
}

我们在尝试用 泛型 实现上面的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 泛型实现
struct Stack<T> {
var items = [T]()
// 入栈
mutating func push(_ item: T) {
items.append(item)
}
// 出栈
mutating func pop() -> T?{
if items.isEmpty {
return nil
}
return items.removeLast()
}
}

泛型的优势和强大,暴露无疑

  • where

where语句 主要用于 表明泛型需要满足的条件,即 限制形式参数的要求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// where
protocol StackProtocol {
// 协议中使用类型的占位符
associatedtype Item
// 实例属性
var itemCount: Int { get }
// 出栈
mutating func pop() -> Item?
// 下标获取
func index(of index: Int) -> Item
}
struct Stack: StackProtocol {
// 在使用时,需要指定具体的类型
typealias Item = Int
// 存储属性
var items = [Item]()
// 计算属性
var itemCount: Int {
get {
return items.count
}
}
// 入栈
mutating func push(_ item: Item){
items.append(item)
}
// 出栈
mutating func pop() -> Item?{
if items.isEmpty {
return nil
}
return items.removeLast()
}
// 下标获取
func index(of index: Int) -> Item{
return items[index]
}
}
/*
whrer语句
T1.Item == T2.Item 表示 T1 和 T2 中的类型必须相等
T1.Item: Equatable 表示 T1 的类型必须遵循 Equatable 协议,意味着 T2 也要遵循 Equatable协议
*/
func compare<T1: StackProtocol, T2: StackProtocol>(_ stack1: T1,_ stack2: T2) -> Bool where T1.Item == T2.Item, T1.Item: Equatable {
// 如果数量相等,则遍历
guard stack1.itemCount == stack2.itemCount else {
return false
}
// 遍历
for i in 0..<stack1.itemCount {
if stack1.index(of: i) != stack2.index(of: i) {
return false
}
}
return true
}

还可以这么写:

1
2
// 扩展协议中的 Item 遵循 Equatable
extension StackProtocol where Item: Equatable {}

当希望 泛型指定类型 拥有特定功能,可以这么写,在上述写法的基础上 增加extension:

1
2
// 扩展协议中 Item 指定具体类型
extension StackProtocol where Item == Int {}

泛型函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 简单的泛型函数
func testGen<T>(_ value: T) -> T {
let tmp = value
return tmp
}
class Teacher {
var age: Int = 18
var name: String = "ZJ"
}
// 传入Int类型
testGen(10)
// 传入元组
testGen((1,2))
// 传入实例对象
testGen(Teacher())

从以上代码可以看出,泛型函数 可以接受任何类型

总结

  • 泛型主要用于解决代码的 抽象能力,以及提升代码的 复用性
  • 如果一个泛型 遵循了某个协议,则在使用时,要求具体的类型也是必须遵循某个协议的;
  • 在定义协议时,可以使用 关联类型 给协议中用到的 类型 起一个 占位符名称
  • where语句 主要用于表明泛型需要满足的条件,即 限制形式参数的要求
  • Post title:Swift学习41:泛型
  • Post author:张建
  • Create time:2023-03-11 04:40:24
  • Post link:https://redefine.ohevan.com/2023/03/11/Swift课程/Swift学习41:泛型/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.
On this page
Swift学习41:泛型