泛型的概念
泛型代碼可根據(jù)自定義需求,寫(xiě)出適用于任何類(lèi)型、靈活且可重用的函數(shù)和類(lèi)型,避免重復(fù)的代碼,用一種清晰和抽象的思維表達(dá)代碼的意思
泛型函數(shù)
示例:
/// 交換變量的值
func exchange<T>(_ one: inout T, _ two: inout T) {
(one, two) = (two, one)
}
var a = 10.0
var b = 20.0
print("a = \(a), b = \(b)") // a = 10.0, b = 20.0
exchange(&a, &b) // 交換a和b的值
print("a = \(a), b = \(b)") // a = 20.0, b = 10.0
var c = "hellow"
var d = "world"
print("a = \(c), b = \(d)") // a = hellow, b = world
exchange(&c, &d) // 交換c和d的值
print("a = \(c), b = \(d)") // a = world, b = hellow
上述代碼中,
exchange(_:_:)函數(shù)就是一個(gè)泛型函數(shù)-
<T>中的<T>是一個(gè)占位類(lèi)型, 在定義過(guò)程中不確定具體的類(lèi)型, 只有在函數(shù)調(diào)用時(shí), 根據(jù)傳入的值的類(lèi)型, 來(lái)推斷出T的具體類(lèi)型-
exchange(&a, &b)中T是Double類(lèi)型 -
exchange(&c, &d)中T是String類(lèi)型
-
注意: 泛型函數(shù)在調(diào)用的時(shí)候, 會(huì)根據(jù)傳入的值推斷出對(duì)應(yīng)的類(lèi)型
- 泛型函數(shù)格式:
func 函數(shù)名<占位類(lèi)型列表>(參數(shù)列表) {
// 函數(shù)體
}
- 注意:
- 參數(shù)列表中,
占位類(lèi)型列表中的占位類(lèi)型必須在參數(shù)列表中使用 - 如果
參數(shù)列表中, 多個(gè)參數(shù)都屬于相同的占位類(lèi)型, 那么這些參數(shù)必需傳入一致的類(lèi)型數(shù)據(jù), 例如: 全部傳入Int,String, 或Double等 - 占位類(lèi)型列表可以有多個(gè)占位類(lèi)型, 使用逗號(hào)分開(kāi)
- 參數(shù)列表中,
類(lèi)型參數(shù)
-
exchange(_:_:)函數(shù)中, 占位類(lèi)型T就是一個(gè)類(lèi)型參數(shù)的例子, 即:T是一個(gè)類(lèi)型參數(shù) -
類(lèi)型參數(shù)指定并命名一個(gè)占位類(lèi)型, 并且緊隨在函數(shù)名后面, 使用一對(duì)尖括號(hào)括起來(lái)(例如:<T>) - 一旦一個(gè)類(lèi)型參數(shù)被指定, 就可以如下使用:
- 用來(lái)定義一個(gè)函數(shù)的參數(shù)類(lèi)型(例如:
exchange(_:_:)中的one和two的類(lèi)型) - 做為函數(shù)的返回值
- 函數(shù)主體中的注釋類(lèi)型
- 用來(lái)定義一個(gè)函數(shù)的參數(shù)類(lèi)型(例如:
- 類(lèi)型參數(shù)的定義過(guò)程中不會(huì)代表任何具體的類(lèi)型, 只是一個(gè)占位, 當(dāng)函數(shù)被調(diào)用時(shí), 會(huì)根據(jù)傳入的值的類(lèi)型, 推斷出具體類(lèi)型, 例如上面的
Double和Stirng替換掉T - 參數(shù)類(lèi)型可以同時(shí)存在多個(gè), 并用逗號(hào)分開(kāi), 例如: <T, U, S>為三個(gè)類(lèi)型參數(shù)(占位類(lèi)型), 名稱(chēng)分別為
參數(shù)類(lèi)型T,參數(shù)類(lèi)型U,參數(shù)類(lèi)型S
綜上有泛型函數(shù)格式如下:
func 函數(shù)名<類(lèi)型參數(shù)列表>(參數(shù)列表) { // 函數(shù)體 }
泛型類(lèi)型
- 泛型函數(shù)是在函數(shù)名的后面緊跟著
類(lèi)型參數(shù)列表, 而泛型類(lèi)型就是在定義的類(lèi)型的時(shí)候, 在類(lèi)型名后面緊跟類(lèi)型參數(shù)列表
示例
- 泛型類(lèi):
泛型類(lèi):
class GenericClass<Element> {
// 集合
var items = [Element]()
// 壓棧
func push(_ item: Element) {
items.append(item)
}
// 出棧
func pop() -> Element? {
return items.isEmpty ? nil : items.removeLast()
}
}
- 泛型結(jié)構(gòu)體
泛型結(jié)構(gòu)體:
struct GenericStruct<Element> {
// 集合
var items = [Element]()
// 壓棧
mutating func push(_ item: Element) {
items.append(item)
}
// 出棧
mutating func pop() -> Element? {
return items.isEmpty ? nil : items.removeLast()
}
}
- 泛型枚舉:
泛型枚舉:
enum GenericEnum<Element> {
case none
case some(Element)
}
- 上面的 GenericClass(類(lèi)), GenericStruct(結(jié)構(gòu)體), GenericEnum(枚舉)都是泛型類(lèi)型, 在類(lèi)型名后緊跟著泛型的類(lèi)型參數(shù) <Element>
- 在
泛型類(lèi)型使用的時(shí)候, 需要指定類(lèi)型參數(shù)的具體類(lèi)型, 下面以結(jié)構(gòu)體GenericStruct為例:
// 創(chuàng)建GenericStruct類(lèi)型的機(jī)構(gòu)體變量struct
// 指定類(lèi)型參數(shù)為 Int
var struct = GenericStruct<Int>()
// 使用struct時(shí), push(_:), pop()方法使用 類(lèi)型參數(shù)的地方 都會(huì)替換為Int類(lèi)型
struct.push(1)
struct.push(2)
struct.push(3)
struct.push("4") // 報(bào)錯(cuò): 因?yàn)閜ush(_:)接收的參數(shù)類(lèi)型已經(jīng)被替換成Int
let result = struct.pop() // result = 3
給泛型類(lèi)型添加分類(lèi)(extension)
- 泛型類(lèi)型添加分類(lèi)時(shí), 定義中不可以增加新的類(lèi)型參數(shù), 也不需要寫(xiě)已有類(lèi)型參數(shù)(編譯器也不允許寫(xiě))
- 錯(cuò)誤寫(xiě)法:
錯(cuò)誤一: 分類(lèi)的定義中不可以增加新的類(lèi)型參數(shù)
extension GenericClass<T> { }
錯(cuò)誤二: 分類(lèi)的定義中不需要寫(xiě)已經(jīng)有的類(lèi)型參數(shù)
extension GenericClass<Element> { }
- 下面的代碼是正確寫(xiě)法, 在分類(lèi)中可以使用類(lèi)型定義時(shí)有的類(lèi)型參數(shù):
extension GenericClass {
// 使用已有的 類(lèi)型參數(shù): Element 做為返回值
func element(at index: Int) -> Element? {
if index < items.count {
return items[index]
}else {
return nil
}
}
}
雖然在分類(lèi)中無(wú)法定義新的類(lèi)型參數(shù), 但是可以在分類(lèi)新定義的方法中引入其他的類(lèi)型參數(shù)
extension GenericClass { func exchange<T>(one: inout T, two: inout T) { (one, two) = (two, one) } }
泛型約束
- 上述所有代碼中, 不論是泛型函數(shù)中的
類(lèi)型參數(shù)T, 還是泛型類(lèi)型中的類(lèi)型參數(shù)Element, 都可以在調(diào)用時(shí)指定任意一個(gè)具體的類(lèi)型做為替換, 這是因?yàn)槲也](méi)有給這些參數(shù)類(lèi)型添加任何的約束
那么什么是
參數(shù)類(lèi)型添加約束呢?就拿我們經(jīng)常使用的
Dictionary為例, 我們知道Dictionary的定義中有兩個(gè)參數(shù)類(lèi)型, 分別為Key和Value, 而且在給Dictionary添加元素的時(shí)候,key的值都是唯一的,即Dictionary根據(jù)Key的值來(lái)判斷是修改還是增加元素, 而Swift中的Dictionary是根據(jù)Key的哈希值來(lái)判斷唯一性的, 也就是說(shuō)Dictionary的Key值必須是可哈希的, 所以Dictionary的類(lèi)型參數(shù)Key有一個(gè)約束, 那就是 可哈希的值
- 下面是
Dictionary定義的代碼部分, 這里過(guò)濾里面的實(shí)現(xiàn)部分, 其中的where會(huì)在后面講解
public struct Dictionary<Key, Value> : Collection, ExpressibleByDictionaryLiteral where Key : Hashable{}
類(lèi)型約束語(yǔ)法
- 在定義一個(gè)類(lèi)型參數(shù)時(shí), 在類(lèi)型參數(shù)后面放置一個(gè)類(lèi)名或者協(xié)議名, 并用冒號(hào)分開(kāi), 來(lái)定義
類(lèi)型參數(shù)的類(lèi)型約束, 他們將成為類(lèi)型參數(shù)列表的一部分 - 示例:
泛型函數(shù)添加類(lèi)型約束
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// 這里是反省函數(shù)的函數(shù)體部分
}
泛型類(lèi)型添加類(lèi)型約束
class GenericClass<T: SomeClass, U: SomeProtocol> {
// 類(lèi)的實(shí)現(xiàn)部分
}
- 上面的
泛型函數(shù)和泛型類(lèi)型都分別有兩個(gè)類(lèi)型參數(shù)T和U,T有一個(gè)類(lèi)型約束: 必須是SomeClass類(lèi)的子類(lèi);U有一個(gè)類(lèi)型約束: 必須遵守SomeProtocol協(xié)議的類(lèi)型
類(lèi)型約束實(shí)踐
- 現(xiàn)在有一個(gè)非泛型函數(shù)
findIndex(ofString:in:), 該函數(shù)的功能是在一個(gè)String數(shù)組中查找給定的String值的索引, 若找到匹配的String值, 會(huì)返回該String值在String數(shù)組中的索引, 否則返回nil
func findIndex(ofString valueToFind: String, in array: [String]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
- findIndex(ofString:in:)函數(shù)可以用于查找字符串?dāng)?shù)組中某個(gè)字符串的索引值
let strings = ["a", "b", "c", "d", "e"]
if let foundIndex = findIndex(of: "c", in: strings) {
print("c 的索引值是 \(foundIndex)")
}
// 打印: c 的索引值是 2
- 我們知道,
findIndex(ofString:in:)函數(shù)目前只能查找字符串在數(shù)組中的索引值, 用處不是很大。不過(guò), 我們可以用占位類(lèi)型T替換掉String類(lèi)型來(lái)寫(xiě)出具有相同功能的泛型函數(shù)findIndex(of:in:) - 下面就是使用占位類(lèi)型T替換掉String類(lèi)型的代碼:
func findIndex<T>(of valueToFind: T, in array: [T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
這段代碼看上去可以查找任意的具體類(lèi)型在該類(lèi)型數(shù)組中的索引值, 但是在Xcode中并不能正常運(yùn)行
這是因?yàn)樵赟wift中, 想要比較兩個(gè)值是否相等, 那么這兩個(gè)值的類(lèi)型必須實(shí)現(xiàn)了
Equatable協(xié)議才可以對(duì)于未實(shí)現(xiàn)
Equatable協(xié)議的類(lèi)型, 比如我們自定義的類(lèi)和結(jié)構(gòu)體的實(shí)例, 是不能直接使用==來(lái)比較的, 因?yàn)檫@些實(shí)例并不知道"相等"意味著什么, 是部分內(nèi)容相等才相等, 還是完全相等才算相等, 而Equatable就是用來(lái)說(shuō)明"相等"意味著什么的因?yàn)橹挥凶袷?code>Equatable協(xié)議的類(lèi)型才能進(jìn)行相等判斷, 所以上述可以被替換成為任意類(lèi)型的T就不能符合要求, 所以我們需要給T加上一個(gè)類(lèi)型約束: 想要替換占位類(lèi)型T的具體類(lèi)型, 必須遵守
Equatable協(xié)議任何遵守
Equatable協(xié)議的類(lèi)型都可以在findIndex(of:in:)中正常運(yùn)行, 代碼如下:
func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
-
findIndex(of:in:)唯一的類(lèi)型參數(shù)叫做T: Equatable, 即: 任意符合Equatable協(xié)議的類(lèi)型T
關(guān)聯(lián)類(lèi)型(泛型協(xié)議)
類(lèi)、結(jié)構(gòu)體和枚舉的泛型類(lèi)型中, 將
類(lèi)型參數(shù)列表放在了類(lèi)型名的后面, 而在泛型協(xié)議中卻不能這樣寫(xiě)泛型協(xié)議的寫(xiě)法與泛型類(lèi)型有所不同, 需要使用associatedtype關(guān)鍵字來(lái)指定類(lèi)型參數(shù)泛型協(xié)議中的類(lèi)型參數(shù)又被稱(chēng)為關(guān)聯(lián)類(lèi)型, 其代表的實(shí)際類(lèi)型在協(xié)議被采納時(shí)才會(huì)被指定下面一段代碼就是有關(guān)聯(lián)類(lèi)型的協(xié)議:
protocol Container {
associatedtype ItemType
mutating func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
-
協(xié)議
Container定義了三個(gè)任何采納該協(xié)議的類(lèi)型必須提供的功能必須可以通過(guò)append(_:)方法添加一個(gè)類(lèi)型為
ItemType的新元素到容器里必須可以通過(guò)count屬性獲取容器中元素的數(shù)量, 并返回一個(gè)Int值
必須可以通過(guò)索引值類(lèi)型為Int的下標(biāo)檢索到容器中的每一個(gè)類(lèi)型為
ItemType的元素
協(xié)議中無(wú)法定義
ItemType的具體類(lèi)型, 而任何遵從Container協(xié)議的類(lèi)型都必須指定關(guān)聯(lián)類(lèi)型ItemType的具體類(lèi)型下面的是一個(gè)非泛型的IntStack結(jié)構(gòu)體, 采納并符合了
Container協(xié)議, 實(shí)現(xiàn)了Container協(xié)議的是三個(gè)要求:
struct IntStack: Container {
// 集合數(shù)組, 用于存放元素
var items = [Int]()
// Container協(xié)議部分
typealias ItemType = Int // 通過(guò)關(guān)鍵字 typealias 指定ItemType的類(lèi)型為Int
mutating func append(item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
IntStack在實(shí)現(xiàn)
Container的要求時(shí), 指定了ItemType的類(lèi)型為Int, 即typealias ItemType = Int, 從而將Container協(xié)議中抽象的ItemType類(lèi)型轉(zhuǎn)換為具體的Int類(lèi)型由于Swift的類(lèi)型推斷, 實(shí)際上不用再
IntStack中特意的聲明ItemType的類(lèi)型為Int也可以, 這是因?yàn)?code>IntStack符合Container協(xié)議的所有要求, 并且在方法中也將ItemType寫(xiě)成了Int類(lèi)型, 這樣Swift就可以推斷出ItemType的類(lèi)型為Int, 事實(shí)上, 在代碼中刪除typeealias IntType = Int這一行, 一切依舊可以正常工作
struct IntStack: Container {
var items = [Int]()
mutating func append(item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
- 上面的代碼自動(dòng)推斷出
IntType的類(lèi)型是Int
即: 對(duì)于泛型協(xié)議, 當(dāng)有類(lèi)型遵從該協(xié)議的時(shí)候, 只需要給未確定具體類(lèi)型的關(guān)聯(lián)類(lèi)型所參與的所有方法中, 都給出唯一指定類(lèi)型時(shí), 并不需要特意聲明該關(guān)聯(lián)類(lèi)型的聲明也能正常運(yùn)行, 原因就是Swift的自動(dòng)推斷
- 也可以讓泛型Stack結(jié)構(gòu)體遵從Container協(xié)議
struct Stack<Element>: Container {
var items = [Element]()
// 由于所有需要關(guān)聯(lián)類(lèi)型的地方都指定了明確類(lèi)型, 就不需要在特意的聲明關(guān)聯(lián)類(lèi)型具體是什么類(lèi)型了, 這里自動(dòng)推斷出 ItemType的類(lèi)型是Stack對(duì)象創(chuàng)建時(shí)指定的泛型具體類(lèi)型
// typealias ItemType = Element
mutating func append(item: Element) {
self.items.append(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
通過(guò)擴(kuò)展一個(gè)存在的類(lèi)型來(lái)指定關(guān)聯(lián)類(lèi)型
- 對(duì)于一個(gè)已經(jīng)存在的類(lèi)型, 并且在定義中沒(méi)有遵守
泛型協(xié)議, 我們可以在它的extension中遵守需要的泛型協(xié)議, 并且在該擴(kuò)展中 也可以自動(dòng)推導(dǎo)ItemType的類(lèi)型, 并不需要寫(xiě)typealias ItemType = Element
struct Stack<Element> {
var items = [Element]()
}
// 通過(guò)擴(kuò)展遵從泛型協(xié)議 Container
extension Stack: Container {
// 這一行可以不寫(xiě)
// typealias ItemType = Element
mutating func append(item: Element) {
self.items.append(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
泛型 Where 語(yǔ)句
- 上面的敘述中,
類(lèi)型約束讓我們能夠?yàn)?code>泛型函數(shù)或泛型類(lèi)型的類(lèi)型參數(shù)定義一些強(qiáng)制要求- 除了
類(lèi)型約束以外, 還有一種方法給泛型函數(shù)或泛型類(lèi)型的類(lèi)型參數(shù)定義約束, 那就是where子句
- 現(xiàn)有如下方法:
func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
- 該函數(shù)就是
類(lèi)型約束中使用的, 獲取一個(gè)元素, 在該元素?cái)?shù)組中的索引值的函數(shù), 在占位類(lèi)型T后 使用: Equatable對(duì)T進(jìn)行了類(lèi)型約束。 - 現(xiàn)在可以將該函數(shù)使用
where子句變形為下面代碼:
func findIndex<T>(of valueToFind: T, in array: [T]) -> Int? where T : Equatable {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
變形后的代碼, 將
類(lèi)型約束提取出來(lái), 放在了返回值的后面, 使用where子句來(lái)表達(dá)該約束, 這在語(yǔ)法上沒(méi)有任何問(wèn)題, 并且變形后的函數(shù)依然可以正常使用當(dāng)然, 如果僅僅是給
類(lèi)型參數(shù)添加類(lèi)型約束, 僅僅需要第一種方式就可以了。 實(shí)際上where子句還有另外一個(gè)用法, 即:where子句除了給類(lèi)型參數(shù)添加類(lèi)型約束外, 還可以給關(guān)聯(lián)類(lèi)型添加約束
- 通過(guò)下面的代碼示例進(jìn)行講解
where子句給關(guān)聯(lián)類(lèi)型添加約束的用法
// 容器協(xié)議
protocol Container {
associatedtype ItemType
mutating func append(item: ItemType)
}
func allItemsMatch<C1: Container, C2: Container>(_ someContainer: C1, _ anotherContainer: C2) -> Bool {
// 檢查兩個(gè)容器包含相同數(shù)量的元素
if someContainer.count != anotherContainer.count {
return false
}
// 檢查每個(gè)元素是否相等
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
return true
}
- 上述代碼中,
allItemsMatch(_:_:)函數(shù)是一個(gè)泛型函數(shù), 作用是判斷兩個(gè)都遵守了Container協(xié)議的容器C1, C2中所有的元素是否在位置和值上完全相等 - 由于C1和C2是兩個(gè)不同的占位類(lèi)型, 所以C1和C2可以是兩個(gè)不同的類(lèi)型
// 遵守Container協(xié)議的類(lèi)型Stack1
class Stack1<Element>: Container {
var items = [ItemType]()
typealias ItemType = Element
func append(item: Element) {
items.append(item)
}
}
// 遵守Container協(xié)議的類(lèi)型Stack2
class Stack2<Element>: Container {
var items = [ItemType]()
typealias ItemType = Element
func append(item: Element) {
items.append(item)
}
}
上面定義了都遵守
Container協(xié)議的兩個(gè)類(lèi)型Stack1和Stack2, 我們將使用Stack1和Stack2的實(shí)例對(duì)象進(jìn)行比較根據(jù)
allItemsMatch(_:_:)的作用可以判斷出, 兩個(gè)容器Stack1和Stack2只有在元素類(lèi)型(ItemType)類(lèi)型一致的情況下才能判斷元素是否相等, 但是這個(gè)約束使用類(lèi)型約束中的方法無(wú)法添加, 所以就有了下面的寫(xiě)法:
// 這里只考慮定義部分, 不考慮實(shí)現(xiàn)部分
func allItemsMatch<C1: Container, C2: Container>(_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.ItemType == C2.ItemType {//函數(shù)體}
- 這句代碼中, 使用了
where語(yǔ)句對(duì)C1和C2的關(guān)聯(lián)類(lèi)型進(jìn)行了約束, 即C1和C2的ItemType
有了這個(gè)
where子句后, 只有Stack1和Stack2中的元素類(lèi)型必須一致才能使用該函數(shù)
- 當(dāng)然僅僅有類(lèi)型相等判斷是不夠的, 容器中的元素還必須遵守
Equatable才行, 即C1.ItemType == C2.ItemType并且C1.ItemType: Equatable, 所以allItemsMatch(_:_:)函數(shù)的完整代碼如下:
func allItemsMatch<C1: Container, C2: Container>(_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
// 檢查兩個(gè)容器包含相同數(shù)量的元素
if someContainer.count != anotherContainer.count {
return false
}
// 檢查每個(gè)元素是否相等
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
return true
}
- 在where子句中, 使用逗號(hào)分隔多個(gè)約束
類(lèi)型的定義中也可以使用
where子句添加約束, 用法與在泛型函數(shù)中一樣, 都寫(xiě)在定義的后面, 大括號(hào){}的前面
- 示例:
class Stack<Element>: Container where Stack.ItemType : Equatable {
var items = [ItemType]()
typealias ItemType = Element
func append(_ item: Element) {
items.append(item)
}
}
具有泛型where子句的擴(kuò)展
- 你可以使用泛型where子句做為擴(kuò)展的一部分, 基于以前的例子, 下面的示例擴(kuò)展了泛型Stack結(jié)構(gòu)體, 添加一個(gè)isTop:方法
extension Stack where Element: Equatable {
func isTop(_ item: Element) -> Bool {
return items.last == item
}
}
- 以下是 isTop(_:) 方法的調(diào)用方式:
if stackOfStrings.isTop("c") {
print("Top element is c.")
} else {
print("Top element is something else.")
}
// 打印 "Top element is c."
- 如果嘗試在包含的元素不符合Equatable協(xié)議的棧上調(diào)用isTop(_:)方法, 則會(huì)收到編譯時(shí)錯(cuò)誤
struct NotEquatable { }
var notEquatableStack = Stack<NotEquatable>()
let notEquatableValue = NotEquatable()
notEquatableStack.push(notEquatableValue)
notEquatableStack.isTop(notEquatableValue) // 報(bào)錯(cuò)
- 你可以使用where子句擴(kuò)展一個(gè)協(xié)議, 基于以前的實(shí)例, 下面的實(shí)例擴(kuò)展了Container協(xié)議, 添加一個(gè)startsWith(:_)方法
extension Container where ItemType: Equatable {
func startWith(_ item: ItemType) -> Bool {
return count >= 1 && self[0] == item
}
}
extension Array: Container{}
let array = ["a", "b", "c"]
if array.startWith("a") {
print("array 第一個(gè)元素是 a")
}else {
print("array 第一個(gè)元素不是 a")
}
// 打印 array 第一個(gè)元素是 a
- 除了給
泛型類(lèi)型和泛型協(xié)議分類(lèi)中添加上述的where子句外, 還可以直接約束ItemType的具體類(lèi)型:
extension Container where ItemType == Double {
func average() -> Double {
var sum = 0.0
for i in 0..<count {
sum += self[i]
}
return sum / Double(count)
}
}
print([1260.0, 1200.0, 98.6, 37.0].average())
// 打印 "648.9"
- 只有容器中的元素為
Double類(lèi)型時(shí), 才可以調(diào)用該方法
具有泛型 Where 子句的關(guān)聯(lián)類(lèi)型
- 除了上述使用方法外,
where子句還可以直接在泛型協(xié)議的定義中, 直接給泛型協(xié)議的參數(shù)類(lèi)型添加約束
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
associatedtype Iterator: IteratorProtocol where Iterator.Element == Item
func makeIterator() -> Iterator
}
上述代碼
Container協(xié)議中, 通過(guò)associatedtype Iterator定義了一個(gè)類(lèi)型參數(shù)Iterator, 并使用類(lèi)型約束, 使Iterator必須遵從IteratorProtocol協(xié)議, 又使用where子句, 約束了Iterator對(duì)應(yīng)IteratorProtocol協(xié)議的類(lèi)型參數(shù)的類(lèi)型必須和associatedtype Item類(lèi)型一致一個(gè)協(xié)議繼承了另一個(gè)協(xié)議, 你通過(guò)在協(xié)議聲明的時(shí)候, 包含泛型Where子句, 來(lái)添加一個(gè)約束到被繼承協(xié)議的關(guān)聯(lián)類(lèi)型, 例如, 下面的代碼聲明了一個(gè)ComparableContainer協(xié)議, 他要求所有的Item必須是Comparable的
protocol ComparableContainer: Container where Item: Comparable {}