浅谈 Swift 中的泛型

Objective-C 缺少一个重要特性:不支持 泛型 。幸运地是, Swift 拥有这一特性。 泛型 容许你声明的函数、类以及结构体支持不一样的数据类型。

提出问题

优秀的泛型使用案例中,最多见的例子当属对栈(Stack)的操做。栈做为容器有两种操做:一.压入(Push)操做添加项到容器中;二.弹出(Pop)操做将最近添加项从容器移除。首先咱们用非泛型方式设计。最后代码以下所示:web

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class IntStack{
// 采用数组做为容器保存数据 类型为Int
private var stackItems:[Int] = []
// 入栈操做 即Push 添加最新数据到容器最顶部
func pushItem(item:Int){
stackItems.append(item)
}
// 出栈操做 即Pop 将容器最顶部数据移除
func popItem()->Int?{
let lastItem = stackItems.last
stackItems.removeLast()
return lastItem
}
}

该栈可以处理Int类型数据。这看起来不错,可是假若要创建一个可以处理String类型的,咱们又该如何实现呢?咱们须要替换全部IntString,不过这显然是一个糟糕的解决方法。此外另一种方法乍看之下灰常不错,以下:编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class AnyObjectStack{
// 采用数组做为容器保存数据 类型为AnyObject
private var stackItems:[AnyObject] = []
// 入栈操做 即Push 添加最新数据到容器最顶部
func pushItem(item:AnyObject){
stackItems.append(item)
}
// 出栈操做 即Pop 将容器最顶部数据移除
func popItem()->AnyObject?{
let lastItem = stackItems.last
stackItems.removeLast()
return lastItem
}
}

此处,咱们合理地使用AnyObject类型,那么如今可以将String类型数据压入到栈中了,对么?不过这种状况下咱们就失去了数据类型的安全,而且每当咱们对栈进行操做时,都须要进行一系列繁琐的类型转换(casting操做,使用as来进行类型转换)。swift

解决方案

参照泛型的特性,咱们可以定义一个泛型类型,这看起来像一个占位符。使用泛型后的示例代码以下:数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Stack<T> {

private var stackItems: [T] = []

func pushItem(item:T) {
stackItems.append(item)
}

func popItem() -> T? {
let lastItem = stackItems.last
stackItems.removeLast()
return lastItem
}

}

泛型定义方式:由一对尖括号(<>)包裹,命名方式一般为大写字母开头(这里咱们命名为T)。在初始化阶段,咱们经过明确的类型(这里为Int)来定义参数,以后编译器将全部的泛型T替换成Int类型:安全

1
2
3
4
5
6
7
8
// 指定了泛型T 就是 Int 
// 编译器会替换全部T为Int
let aStack = Stack<Int>()

aStack.pushItem(10)
if let lastItem = aStack.popItem() {
print("last item: \(lastItem)")
}

如此实现的栈,最大优点在于可以匹配任何类型。app

类型约束

这里存在一个缺点:尽管泛型可以表明任何类型,咱们对它的操做也是比较有局限性的。仅仅是比较两个泛型都是不支持的,请看以下代码:编程语言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Stack<T> {

private var stackItems: [T] = []

func pushItem(item:T) {
stackItems.append(item)
}

func popItem() -> T? {
let lastItem = stackItems.last
stackItems.removeLast()
return lastItem
}

func isItemInStack(item:T) -> Bool {
var found = false
for stackItem in stackItems {
if stackItem == item { //编译报错!!!!!!!!!!
found = true
}
}
return found
}
}

注意到函数isItemInSatck(item:T)中,咱们获得了一个编译错误,由于两个参数没有实现Equtable协议的话,类型值是不能进行比较的。实际上咱们能够为泛型增长约束条件来解决这个问题。在本例中,经过对第一行进行修改,咱们让泛型T遵循Equatable协议:函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Stack<T:Equatable> {

private var stackItems: [T] = []

func pushItem(item:T) {
.append(item)
}

func popItem() -> T? {
let lastItem = stackItems.last
stackItems.removeLast()
return lastItem
}

func isItemInStack(item:T) -> Bool {
var found = false
for stackItem in stackItems {
if stackItem == item {
ound = true
}
}
return found
}
}

总结

就像众多其余编程语言同样,你也可以在Swift中利用泛型这一特性。假若你想要写一个库,泛型是很是好用的特性。ui


 


相关文章
相关标签/搜索