作者:Thomas Hanning,原文鏈接,原文日期:2015/09/09
譯者:pmst;校對:numbbbbb;定稿:shanks
Objective-C缺乏一個(gè)重要特性:不支持泛型。幸運(yùn)地是,Swift擁有這一特性。泛型允許你聲明的函數(shù)、類以及結(jié)構(gòu)體支持不同的數(shù)據(jù)類型。
提出問題
優(yōu)秀的泛型使用案例中,最常見的例子當(dāng)屬對棧(Stack)的操作。棧作為容器有兩種操作:一.壓入(Push)操作添加項(xiàng)到容器中;二.彈出(Pop)操作將最近添加項(xiàng)從容器移除。首先我們用非泛型方式設(shè)計(jì)棧。最后代碼如下所示:
class IntStack{
// 采用數(shù)組作為容器保存數(shù)據(jù) 類型為Int
private var stackItems:[Int] = []
// 入棧操作 即Push 添加最新數(shù)據(jù)到容器最頂部
func pushItem(item:Int){
stackItems.append(item)
}
// 出棧操作 即Pop 將容器最頂部數(shù)據(jù)移除
func popItem()->Int?{
let lastItem = stackItems.last
stackItems.removeLast()
return lastItem
}
}
該棧能夠處理Int類型數(shù)據(jù)。這看起來不錯(cuò),但是倘若要建立一個(gè)能夠處理String類型的棧,我們又該如何實(shí)現(xiàn)呢?我們需要替換所有Int為String,不過這顯然是一個(gè)糟糕的解決方法。此外另外一種方法乍看之下灰常不錯(cuò),如下:
class AnyObjectStack{
// 采用數(shù)組作為容器保存數(shù)據(jù) 類型為AnyObject
private var stackItems:[AnyObject] = []
// 入棧操作 即Push 添加最新數(shù)據(jù)到容器最頂部
func pushItem(item:AnyObject){
stackItems.append(item)
}
// 出棧操作 即Pop 將容器最頂部數(shù)據(jù)移除
func popItem()->AnyObject?{
let lastItem = stackItems.last
stackItems.removeLast()
return lastItem
}
}
此處,我們合理地使用AnyObject類型,那么現(xiàn)在能夠?qū)?code>String類型數(shù)據(jù)壓入到棧中了,對么?不過這種情況下我們就失去了數(shù)據(jù)類型的安全,并且每當(dāng)我們對棧進(jìn)行操作時(shí),都需要進(jìn)行一系列繁瑣的類型轉(zhuǎn)換(casting操作,使用as來進(jìn)行類型轉(zhuǎn)換)。
解決方案
參照泛型的特性,我們能夠定義一個(gè)泛型類型,這看起來像一個(gè)占位符。使用泛型后的示例代碼如下:
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)來定義參數(shù),之后編譯器將所有的泛型T替換成Int類型:
// 指定了泛型T 就是 Int
// 編譯器會(huì)替換所有T為Int
let aStack = Stack<Int>()
aStack.pushItem(10)
if let lastItem = aStack.popItem() {
print("last item: \(lastItem)")
}
如此實(shí)現(xiàn)的棧,最大優(yōu)勢在于能夠匹配任何類型。
類型約束
這里存在一個(gè)缺點(diǎn):盡管泛型能夠代表任何類型,我們對它的操作也是比較有局限性的。僅僅是比較兩個(gè)泛型都是不支持的,請看如下代碼:
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 { //編譯報(bào)錯(cuò)!!!!!!!!!!
found = true
}
}
return found
}
}
注意到函數(shù)isItemInSatck(item:T)中,我們得到了一個(gè)編譯錯(cuò)誤,因?yàn)閮蓚€(gè)參數(shù)沒有實(shí)現(xiàn)Equtable協(xié)議的話,類型值是不能進(jìn)行比較的。實(shí)際上我們可以為泛型增加約束條件來解決這個(gè)問題。在本例中,通過對第一行進(jìn)行修改,我們讓泛型T遵循Equatable協(xié)議:
class Stack<T:Equatable> {
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
}
}
總結(jié)
就像眾多其他編程語言一樣,你也能夠在Swift中利用泛型這一特性。倘若你想要寫一個(gè)庫,泛型是非常好用的特性。