淺談 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


 


相關文章
相關標籤/搜索