本文轉(zhuǎn)載自http://blog.csdn.net/youshaoduo/article/details/54863470
泛型能夠讓開發(fā)者編寫自定義需求已經(jīng)任意類型的靈活可用的的函數(shù)和類型。能夠讓我們避免重復(fù)的代碼。用一種清晰和抽象的方式來表達(dá)代碼的意圖。
下面是一個(gè)非泛型的例子
[objc]view plaincopy
func?swapTwoIntValue(a:?inout?Int,b:?inout?Int){
let?tempValue?=?a
a?=?b
b?=?tempValue
}
這個(gè)函數(shù)用來交換兩個(gè)整形的數(shù)值
[objc]view plaincopy
var?a?=1
var?b?=2
swapTwoIntValue(a:?&a,b:?&b)
print(a,?b)//2,1
對(duì)于這個(gè)例子,假如我們想交換兩個(gè)Double類型、或者是其他類型的值,就需要針對(duì)每個(gè)類型寫不同的方法,只是參數(shù)類型不同。為了解決這個(gè)問題,Swift提供了泛型,幫助我們來解決這個(gè)問題。
注意:Swift是安全的語言,不允許兩個(gè)不同類型互換值
下面是一個(gè)泛型的函數(shù)
[objc]view plaincopy
func?swapTwoValue(a:?inout?T,b:?inout?T){
let?tempValue?=?a
a?=?b
b?=?tempValue
}
這個(gè)函數(shù)主體、功能跟上面的例子類似,用來交換兩個(gè)同樣類型的值,但是這個(gè)函數(shù)用 T 占位符來代替實(shí)際的類型。并沒有指定具體的類型,但是傳入的a ,b 必須是同一類型T。在調(diào)用這個(gè)函數(shù)的時(shí)候才能指定 T 是那種具體的類型。還有函數(shù)名后跟的那個(gè) 是函數(shù)定義的一個(gè)占位類型名,并不會(huì)查找T的具體類型
[objc]view plaincopy
swapTwoValue(&oneInt,b:?&twoInt)
print("oneInt:\(oneInt),twoInt:\(twoInt)")//?oneInt:3,twoInt:4
var?oneStr?="hello"
var?twoStr?="world"
swapTwoValue(&oneStr,b:?&twoStr)
print("oneStr:\(oneStr),twoStr:\(twoStr)")//?oneStr:world,twoStr:hello
var?oneDouble?=10.01
var?twoDouble?=20.02
swapTwoValue(&oneDouble,b:?&twoDouble)
print("oneDouble:\(oneDouble),twoDouble:\(twoDouble)")//?oneDouble:20.02,twoDouble:10.01
這個(gè)例子用泛型完美的解決了上述的問題,只需要定義一個(gè)泛型函數(shù),只要保證 傳入的兩個(gè)參數(shù)是同一個(gè)類型,就不用根據(jù)傳入?yún)?shù)的類型不同而寫不同的方法。
在上面的泛型函數(shù)例子中,占位符T是類型參數(shù)的一個(gè)例子。類型參數(shù)指定并命名一個(gè)占位符類型,并用<>包裹,放在函數(shù)名后面。一旦一個(gè)參數(shù)類型確定,就可以指定參數(shù)類型,或者返回值的類型,還可以用作函數(shù)體的注釋類型。在調(diào)用的時(shí)候會(huì)被實(shí)際的類型替代,如傳遞的是Int,就替換為Int,如果傳入的是Double類型就替換為Double等等
上面的泛型例子的 T,只是一個(gè)描述性的名字,通常用單一的字母來命名,例如:T、U、V等。T代表只是一個(gè)占位符,命名規(guī)則同駝峰命名法
除了定義泛型函數(shù),還可以定義泛型類型。如Array,Dictionary的用法
下面展示一個(gè)非泛型版本棧
[objc]view plaincopy
structIntStack?{
var?items?=?[Int]()
mutating?func?push(item:?Int)?{
items.append(item)
}
mutating?func?pop(item:?Int)?->?Int?{
returnitems.removeLast()
}
}
這個(gè)是一個(gè)泛型版本的棧
[objc]view plaincopy
structStack?{
var?items?=?[T]()
mutating?func?push(item:?T)?{
items.append(item)
}
mutating?func?pop(item:?T)?->?T?{
returnitems.removeLast()
}
}
首先在函數(shù)名后面加<泛型類型名>,<>里面表明類型參數(shù)名。然后在函數(shù)主體里面完成跟非泛型棧類似的功能。這樣這個(gè)泛型結(jié)構(gòu)體就不只能壓棧Int類型的值,還可以是其它類型
[objc]view plaincopy
var?stack?=?Stack()//要在類型名后面加<類型名>
stack.push("uno")
stack.push("dos")
stack.push("tres")
stack.push("cuatro")
print(stack.pop())//?cuatro
可以擴(kuò)展一個(gè)泛型類型,給這個(gè)泛型類型添加屬性、方法、下標(biāo)等。
[objc]view plaincopy
extension?Stack{
//給泛型Stack擴(kuò)展一個(gè)計(jì)算型屬性topItem,返回最上面的item
vartopItem:?T??{
returnitems.isEmpty?nil:?items[items.count-1]
}
}
在上面的SwapTwoVlues 和 Stack中,可以作用任何類型。但是也可以添加一個(gè)約束,比如指定一個(gè)類型必須采納某協(xié)議或者是指定類等。在Swift中(Bool,Int,Doube,String默認(rèn)都是哈希的),Dictionary的鍵就需要必須是可哈希的,方便字典查找是否已包含某個(gè)鍵的值。
類型約束語法
可以在類型參數(shù)名后面加一個(gè)類型或者協(xié)議名,通過冒號(hào)隔開,多個(gè)類型參數(shù)之間用逗號(hào)隔開
[objc]view plaincopy
func?somFuntion(someClass:?C,someProtocol:?P){
//這里用NSObject和NSObjectProtocol做例子
}
在定義的這個(gè)函數(shù)中,有兩個(gè)類型約束,第一次類型參數(shù)C代表是某個(gè)類,第二個(gè)參數(shù)P代表某個(gè)協(xié)議。
類型約束實(shí)踐
這個(gè)非泛型類型的方法用來查找某個(gè)字符串是否在字符數(shù)組中,存在返回index
[objc]view plaincopy
func?findStrInArray(_array:?[String],strToFind:?String)?->?Int?{
for(index,value)?in?array.enumerated(){
ifstrToFind?==?value{
returnindex
}
}
returnnil
}
下面這是針對(duì)上面非泛型方法泛型版本的方法
[objc]view plaincopy
func?findIndex?(_array:?[T],?_valueToFind:?T)?->?Int??{
for(index,value)?in?array.enumerated(){
ifvalue?==?valueToFind?{//如果沒指定S:Equatable?這句話會(huì)編譯不通過
returnindex
}
}
returnnil
}
在這個(gè)泛型例子中,不是所有的類型都可以 用 == 來比較的,所有必須指定泛型類型參數(shù)的約束為 Swift提供的 Equatable 協(xié)議,這表示T代表的類型必須是支持 Equatable 協(xié)議的。所有的Swift標(biāo)準(zhǔn)類型默認(rèn)都是支持Equatable協(xié)議的.
[objc]view plaincopy
let?value?=?findIndex([3.14159,0.1,0.25],9.3)
//?doubleIndex?類型為?Int?,其值為?nil,因?yàn)?9.3?不在數(shù)組中
let?stringIndex?=?findIndex(["Mike","Malcolm","Andrea"],"Andrea")
//?stringIndex?類型為?Int?,其值為?2
在定義協(xié)議的時(shí)候,有時(shí)候用一個(gè)或者多個(gè)關(guān)聯(lián)類型作為定義協(xié)議的一部分,關(guān)聯(lián)類型作為協(xié)議的一部分,為某個(gè)類型提供了一個(gè)占位符,其實(shí)際類型會(huì)在采納的時(shí)候被指定。并用關(guān)鍵字typealias 關(guān)鍵字來指定關(guān)聯(lián)名
關(guān)聯(lián)類型實(shí)踐
下面定義一個(gè)協(xié)議,協(xié)議指定了一個(gè)關(guān)聯(lián)類型
[objc]view plaincopy
protocol?Container{
associatedtype?itemType//聲明一個(gè)關(guān)聯(lián)類型
mutating?func?appended(item:?itemType)
varcount:?Int{?get?}
subscript(i:?Int)?->?itemType?{?get?}
}
下面是非泛型的版本采納 Container 協(xié)議
[objc]view plaincopy
structintStack:?Container?{
//?IntStack?的原始實(shí)現(xiàn)部分
var?items?=?[Int]()
mutating?func?push(item:?Int)?{
items.append(item)
}
mutating?func?pop()?->?Int?{
returnitems.removeLast()
}
//這里沒設(shè)置關(guān)聯(lián)類型的原因是根本不需要設(shè)置,因?yàn)楹艽_定只返回Int型,當(dāng)然你設(shè)置了也沒問題。
//?Container?協(xié)議的實(shí)現(xiàn)部分
mutating?func?appended(item:?Int)?{
self.push(item:?item)
}
varcount:?Int?{
returnitems.count
}
subscript(i:?Int)?->?Int?{
returnitems[i]
}
}
下面是一個(gè)泛型版本的
[objc]view plaincopy
structgenericStack:?Container{
//?genericStack?的原始實(shí)現(xiàn)部分
var?items?=?[T]()
mutating?func?push(item:?T)?{
items.append(item)
}
mutating?func?pop()?->?T?{
returnitems.removeLast()
}
//這是設(shè)置關(guān)聯(lián)類型具體是什么類型
typealias?itemType?=?T
//?Container?協(xié)議的實(shí)現(xiàn)部分
mutating?func?appended(item:?T)?{
self.push(item:?item)
}
varcount:?Int?{
returnitems.count
}
subscript(i:?Int)?->?T?{
returnitems[i]
}
}
通過擴(kuò)展一個(gè)存在類型來指定關(guān)聯(lián)類型
通過擴(kuò)展添加協(xié)議的一致性描述了如何利用一個(gè)已存在類型符合一個(gè)協(xié)議,這包括了使用關(guān)聯(lián)協(xié)議
Swift中的Array都滿足了Container協(xié)議的要求,意味著可以擴(kuò)展Array采納Container協(xié)議,你可以通過一個(gè)空擴(kuò)展來實(shí)現(xiàn)這點(diǎn).
[objc]view plaincopy
extension?Array?:Container{
mutating?internal?func?appended(item:?Element)?{}
}
定義這個(gè)擴(kuò)展之后,可以用Array當(dāng)做Container類型使用。
9.Where子句
類型約束能夠讓我們?yōu)榉盒皖愋吞砑右恍┘s束和條件。為關(guān)聯(lián)類型添加一些約束也是很有必要的。可以在參數(shù)列表中使用where子句來為關(guān)聯(lián)類型添加約束。
下面的例子判斷兩個(gè)采納Container協(xié)議的類型是否所有的元素順序及值都相等。
[objc]view plaincopy
func?allItemsMatch(someContainer:?C1,_anotherContainer:?C2)?->?Bool?where?C1.itemType==?C2.itemType,?C1.itemType:?Equatable?{
ifsomeContainer.count!=?anotherContainer.count{
returnfalse
}
fori?in0...someContainer.count-1{
ifsomeContainer[i]?!=?anotherContainer[i]{
returnfalse
}
}
returntrue
}
這個(gè)泛型函數(shù)在類型參數(shù)里面添加了where子句約束,C1,C2都必須是采納Container協(xié)議的類型,并且C1、C2的泛型類型必須相同,而且C1的泛型類型必須是采納Equatable的。
[objc]view plaincopy
var?stackOfStrings?=?genericStack()
stackOfStrings.appended(item:"uno")
stackOfStrings.appended(item:"dos")
stackOfStrings.appended(item:"tres")
var?arrayOfStrings?=?["uno","dos","tres"]//array類型的滿足Container類型,參考上面的extension?Array
ifallItemsMatch(stackOfStrings,?arrayOfStrings)?{
print("All?items?match.")
}else{
print("Not?all?items?match.")
}
//結(jié)果是:All?items?match.