23_泛型

泛型代碼 能夠根據(jù)自定義的需求,編寫(xiě)出適用于任意類型、靈活可重用的函數(shù)及類型。避免代碼的重復(fù)允瞧,用一種清晰和抽象的方式來(lái)表達(dá)代碼的意圖。

Swift 的 ArrayDictionary 都是泛型集合蛮拔。你可以創(chuàng)建一個(gè) Int 數(shù)組述暂,也可創(chuàng)建一個(gè) String 數(shù)組,甚至可以是任意其他 Swift 類型的數(shù)組语泽。同樣的贸典,你也可以創(chuàng)建存儲(chǔ)任意指定類型的字典。

泛型所解決的問(wèn)題

下面是一個(gè)標(biāo)準(zhǔn)的非泛型函數(shù) swapTwoInts(_:_:)踱卵,用來(lái)交換兩個(gè) Int 值:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

它只能交換 Int 值,如果你想要交換兩個(gè) String 值或者 Double值,就不得不寫(xiě)更多的函數(shù)惋砂,例如 swapTwoStrings(_:_:)swapTwoDoubles(_:_:)妒挎,如下所示:

func swapTwoStrings(_ a: inout String, _ b: inout String) {
    let temporaryA = a
    a = b
    b = temporaryA
}
 
func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
    let temporaryA = a
    a = b
    b = temporaryA
}

在實(shí)際應(yīng)用中,通常需要一個(gè)更實(shí)用更靈活的函數(shù)來(lái)交換兩個(gè)任意類型的值西饵,幸運(yùn)的是酝掩,泛型代碼幫你解決了這種問(wèn)題。(這些函數(shù)的泛型版本已經(jīng)在下面定義好了眷柔。)

注意
在上面三個(gè)函數(shù)中期虾,ab 類型必須相同。如果 ab 類型不同驯嘱,那它們倆就不能互換值镶苞。Swift 是類型安全的語(yǔ)言,所以它不允許一個(gè) String 類型的變量和一個(gè) Double 類型的變量互換值鞠评。試圖這樣做將導(dǎo)致編譯錯(cuò)誤茂蚓。

泛型函數(shù)

泛型函數(shù)可以適用于任何類型,下面的 swapTwoValues(_:_:) 函數(shù)是上面三個(gè)函數(shù)的泛型版本:

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

泛型函數(shù)名后面跟著占位類型名(這里用字母 T 來(lái)表示)剃幌,并用尖括號(hào)括起來(lái)(<T>)聋涨。占位類型名沒(méi)指明必須是什么類型,但指明了 ab 必須是同一類型 T负乡, 函數(shù)在調(diào)用時(shí)才會(huì)根據(jù)所傳入的實(shí)際類型決定 T 所代表的類型牍白。

var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
// someInt 現(xiàn)在 107, and anotherInt 現(xiàn)在 3

var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString 現(xiàn)在 "world", and anotherString 現(xiàn)在 "hello"

注意
上面定義的 swapTwoValues(_:_:) 函數(shù)是受 swap(_:_:) 函數(shù)啟發(fā)而實(shí)現(xiàn)的。后者存在于 Swift 標(biāo)準(zhǔn)庫(kù)抖棘,你可以在你的應(yīng)用程序中使用它淹朋。如果你在代碼中需要類似 swapTwoValues(_:_:) 函數(shù)的功能,你可以使用已存在的 swap(_:_:) 函數(shù)钉答。

類型參數(shù)

在上面的 swapTwoValues(_:_:) 例子中础芍,占位類型 T 是類型參數(shù)的一個(gè)例子。類型參數(shù)指定并命名一個(gè)占位類型数尿,并且緊隨在函數(shù)名后面仑性,使用一對(duì)尖括號(hào)括起來(lái)(例如 <T>)。

一旦一個(gè)類型參數(shù)被指定右蹦,你可以用它來(lái)定義一個(gè)函數(shù)的參數(shù)類型(例如 swapTwoValues(_:_:) 函數(shù)中的參數(shù) ab)诊杆,或者作為函數(shù)的返回類型,還可以用作函數(shù)主體中的注釋類型何陆。在這些情況下晨汹,類型參數(shù)會(huì)在函數(shù)調(diào)用時(shí)被實(shí)際類型所替換。

你可提供多個(gè)類型參數(shù)贷盲,將它們都寫(xiě)在尖括號(hào)中淘这,用逗號(hào)分開(kāi)剥扣。

命名類型參數(shù)

在大多數(shù)情況下,類型參數(shù)具有一個(gè)描述性名字铝穷,例如 Dictionary<Key, Value> 中的 KeyValue钠怯,以及 Array<Element> 中的 Element,這可以告訴閱讀代碼的人這些類型參數(shù)和泛型函數(shù)之間的關(guān)系曙聂。然而晦炊,當(dāng)它們之間沒(méi)有有意義的關(guān)系時(shí),通常使用單個(gè)字母來(lái)命名宁脊,例如 T断国、UV榆苞,正如上面演示的 swapTwoValues(_:_:) 函數(shù)中的 T 一樣稳衬。

注意
請(qǐng)始終使用大寫(xiě)字母開(kāi)頭的駝峰命名法(例如 TMyTypeParameter)來(lái)為類型參數(shù)命名,以表明它們是占位類型语稠,而不是一個(gè)值宋彼。

泛型類型

除了泛型函數(shù),Swift 還允許你定義泛型類型仙畦。這些自定義類输涕、結(jié)構(gòu)體和枚舉可以適用于任何類型,類似于 ArrayDictionary慨畸。

這部分內(nèi)容將向你展示如何編寫(xiě)一個(gè)名為 Stack (棧)的泛型集合類型莱坎。棧是一系列值的有序集合,和 Array 類似寸士,但它相比 Swift 的 Array 類型有更多的操作限制檐什。數(shù)組允許在數(shù)組的任意位置插入新元素或是刪除其中任意位置的元素。而棧只允許在集合的末端添加新的元素(稱之為入)弱卡。類似的乃正,棧也只能從末端移除元素(稱之為棧)。

注意
棧的概念已被 UINavigationController 類用來(lái)構(gòu)造視圖控制器的導(dǎo)航結(jié)構(gòu)婶博。你通過(guò)調(diào)用 UINavigationControllerpushViewController(_:animated:) 方法來(lái)添加新的視圖控制器到導(dǎo)航棧瓮具,通過(guò) popViewControllerAnimated(_:) 方法來(lái)從導(dǎo)航棧中移除視圖控制器。每當(dāng)你需要一個(gè)嚴(yán)格的“后進(jìn)先出”方式來(lái)管理集合凡人,棧都是最實(shí)用的模型名党。

下面展示了如何編寫(xiě)一個(gè)非泛型版本的棧,以 Int 型的棧為例:

struct IntStack {
    var items = [Int]()
    mutating func push(_ item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
}

上面的 IntStack 結(jié)構(gòu)體只能用于 Int 類型挠轴。不過(guò)传睹,可以定義一個(gè)泛型 Stack 結(jié)構(gòu)體,從而能夠處理任意類型的值岸晦。

下面是相同代碼的泛型版本:

struct Stack<Element> {
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}

例如欧啤,要?jiǎng)?chuàng)建一個(gè) String 類型的棧睛藻,可以寫(xiě)成 Stack<String>()

var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
// 棧中現(xiàn)在有 4 個(gè)字符串

移除并返回棧頂部的值 "cuatro",即將其出棧:

let fromTheTop = stackOfStrings.pop()
// fromTheTop 的值為 "cuatro"堂油,現(xiàn)在棧中還有 3 個(gè)字符串

擴(kuò)展一個(gè)泛型類型

當(dāng)你擴(kuò)展一個(gè)泛型類型的時(shí)候修档,你并不需要在擴(kuò)展的定義中提供類型參數(shù)列表碧绞。原始類型定義中聲明的類型參數(shù)列表在擴(kuò)展中可以直接使用府框,并且這些來(lái)自原始類型中的參數(shù)名稱會(huì)被用作原始定義中類型參數(shù)的引用。

下面的例子擴(kuò)展了泛型類型 Stack讥邻,為其添加了一個(gè)名為 topItem 的只讀計(jì)算型屬性迫靖,它將會(huì)返回當(dāng)前棧頂端的元素而不會(huì)將其從棧中移除:

extension Stack {
    var topItem: Element? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}

類型約束

swapTwoValues(_:_:) 函數(shù)和 Stack 類型可以作用于任何類型。需要時(shí)兴使,類型約束可以指定一個(gè)類型參數(shù)必須繼承自指定類系宜,或者符合一個(gè)特定的協(xié)議或協(xié)議組合。

例如发魄,Swift 的 Dictionary 類型對(duì)字典的鍵的類型做了些限制盹牧。字典的鍵的類型必須是可哈希(hashable)的。這是為了便于檢查字典是否已經(jīng)包含某個(gè)特定鍵的值励幼。字典的鍵類型上汰寓,要求必須符合 Hashable 協(xié)議。所有的 Swift 基本類型(例如 String苹粟、Int有滑、DoubleBool)默認(rèn)都是可哈希的。

類型約束語(yǔ)法

你可以在一個(gè)類型參數(shù)名后面放置一個(gè)類名或者協(xié)議名嵌削,并用冒號(hào)進(jìn)行分隔毛好,來(lái)定義類型約束,它們將成為類型參數(shù)列表的一部分苛秕。對(duì)泛型函數(shù)添加類型約束的基本語(yǔ)法如下所示(作用于泛型類型時(shí)的語(yǔ)法與之相同):

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 這里是泛型函數(shù)的函數(shù)體部分
}

上面這個(gè)函數(shù)有兩個(gè)類型參數(shù)肌访。第一個(gè)類型參數(shù) T,有一個(gè)要求 T 必須是 SomeClass 子類的類型約束艇劫;第二個(gè)類型參數(shù) U吼驶,有一個(gè)要求 U 必須符合 SomeProtocol 協(xié)議的類型約束。

類型約束實(shí)踐

下面非泛型函數(shù)港准,功能是在一個(gè) String 數(shù)組中查找給定 String 值的索引旨剥。若查找到匹配的字符串,findIndex(ofString:in:) 函數(shù)返回該字符串在數(shù)組中的索引值浅缸,否則返回 nil

func findIndex(ofString valueToFind: String, in array: [String]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}
let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
if let foundIndex = findIndex(ofString: "llama", in: strings) {
    print("The index of llama is \(foundIndex)")
}
// 打印 “The index of llama is 2”

findIndex(ofString:in:) 函數(shù)的泛型版本 轨帜。

func findIndex<T>(of valueToFind: T, in array:[T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

上面所寫(xiě)的函數(shù)無(wú)法通過(guò)編譯。問(wèn)題出在相等性檢查上衩椒,即 "if value == valueToFind"蚌父。不是所有的 Swift 類型都可以用等式符(==)進(jìn)行比較哮兰。比如說(shuō),如果你創(chuàng)建一個(gè)自定義的類或結(jié)構(gòu)體來(lái)表示一個(gè)復(fù)雜的數(shù)據(jù)模型苟弛,那么 Swift 無(wú)法猜到對(duì)于這個(gè)類或結(jié)構(gòu)體而言“相等”意味著什么喝滞。正因如此,這部分代碼無(wú)法保證適用于每個(gè)可能的類型 T膏秫,當(dāng)你試圖編譯這部分代碼時(shí)會(huì)出現(xiàn)相應(yīng)的錯(cuò)誤右遭。

Swift 標(biāo)準(zhǔn)庫(kù)中定義了一個(gè) Equatable 協(xié)議,該協(xié)議要求任何遵循該協(xié)議的類型必須實(shí)現(xiàn)等式符(==)及不等符(!=)缤削,從而能對(duì)該類型的任意兩個(gè)值進(jìn)行比較窘哈。所有的 Swift 標(biāo)準(zhǔn)類型自動(dòng)支持 Equatable 協(xié)議。

你可以定義一個(gè) Equatable 類型約束作為類型參數(shù)定義的一部分:

func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}
let doubleIndex = findIndex(of: 9.3, in: [3.14159, 0.1, 0.25])
// doubleIndex 類型為 Int?亭敢,其值為 nil滚婉,因?yàn)?9.3 不在數(shù)組中
let stringIndex = findIndex(of: "Andrea", in: ["Mike", "Malcolm", "Andrea"])
// stringIndex 類型為 Int?,其值為 2

關(guān)聯(lián)類型

定義一個(gè)協(xié)議時(shí)帅刀,有的時(shí)候聲明一個(gè)或多個(gè)關(guān)聯(lián)類型作為協(xié)議定義的一部分將會(huì)非常有用让腹。關(guān)聯(lián)類型為協(xié)議中的某個(gè)類型提供了一個(gè)占位名(或者說(shuō)別名),其代表的實(shí)際類型在協(xié)議被采納時(shí)才會(huì)被指定扣溺。你可以通過(guò) associatedtype 關(guān)鍵字來(lái)指定關(guān)聯(lián)類型骇窍。

關(guān)聯(lián)類型實(shí)踐

下面例子定義了一個(gè) Container 協(xié)議,該協(xié)議定義了一個(gè)關(guān)聯(lián)類型 ItemType

protocol Container {
    associatedtype ItemType
    mutating func append(_ item: ItemType)  //添加一個(gè)新元素到容器里
    var count: Int { get }  //獲取容器中元素的數(shù)量
    subscript(i: Int) -> ItemType { get }   //通過(guò) 下標(biāo)檢索對(duì)應(yīng)元素
}

下面是先前的非泛型的 IntStack 類型娇妓,這一版本采納并符合了 Container 協(xié)議:

struct IntStack: Container {
    // IntStack 的原始實(shí)現(xiàn)部分
    var items = [Int]()
    mutating func push(_ item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
    // Container 協(xié)議的實(shí)現(xiàn)部分
    typealias ItemType = Int
    mutating func append(_ item: Int) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Int {
        return items[i]
    }
}

你也可以讓泛型 Stack 結(jié)構(gòu)體遵從 Container 協(xié)議:

struct Stack<Element>: Container {
    // Stack<Element> 的原始實(shí)現(xiàn)部分
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
    // Container 協(xié)議的實(shí)現(xiàn)部分
    mutating func append(_ item: Element) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Element {
        return items[i]
    }
}

Swift 的類型推斷像鸡,占位類型參數(shù) Element 被用作 append(_:) 方法的 item 參數(shù)和下標(biāo)的返回類型」。可以推斷出 Element 的類型即是 ItemType 的類型只估。

通過(guò)擴(kuò)展一個(gè)存在的類型來(lái)指定關(guān)聯(lián)類型

“通過(guò)擴(kuò)展添加協(xié)議一致性” 中描述了如何利用擴(kuò)展讓一個(gè)已存在的類型符合一個(gè)協(xié)議,這包括使用了關(guān)聯(lián)類型的協(xié)議着绷。

Swift 的 Array 類型已經(jīng)提供 append(_:) 方法蛔钙,一個(gè) count 屬性,以及一個(gè)接受 Int 類型索引值的下標(biāo)用以檢索其元素荠医。這三個(gè)功能都符合 Container 協(xié)議的要求吁脱,也就意味著你只需簡(jiǎn)單地聲明 Array 采納該協(xié)議就可以擴(kuò)展 Array,使其遵從 Container 協(xié)議彬向。你可以通過(guò)一個(gè)空擴(kuò)展來(lái)實(shí)現(xiàn)這點(diǎn)兼贡,正如“通過(guò)擴(kuò)展采納協(xié)議”中的描述:

extension Array: Container {}

如同上面的泛型 Stack 結(jié)構(gòu)體一樣,Arrayappend(_:) 方法和下標(biāo)確保了 Swift 可以推斷出 ItemType 的類型娃胆。定義了這個(gè)擴(kuò)展后遍希,你可以將任意 Array 當(dāng)作 Container 來(lái)使用。

約束關(guān)聯(lián)類型

你可以給協(xié)議里的關(guān)聯(lián)類型添加類型注釋里烦,讓遵守協(xié)議的類型必須遵循這個(gè)約束條件凿蒜。

例如禁谦,下面的代碼定義了一個(gè) Item 必須遵循 EquatableContainer 類型:

protocol Container {
    associatedtype Item: Equatable
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

為了遵守了 Container 協(xié)議,Item 類型也必須遵守 Equatable 協(xié)議废封。

泛型 where 語(yǔ)句

“類型約束” 讓你能夠?yàn)榉盒秃瘮?shù)州泊,下標(biāo),類型的類型參數(shù)定義一些強(qiáng)制要求漂洋。

可以在參數(shù)列表中通過(guò) where 子句為關(guān)聯(lián)類型定義約束遥皂。通過(guò) where 子句要求一個(gè)關(guān)聯(lián)類型遵從某個(gè)特定的協(xié)議,以及某個(gè)特定的類型參數(shù)和關(guān)聯(lián)類型必須類型相同氮发。你可以通過(guò)將 where 關(guān)鍵字緊跟在類型參數(shù)列表后面來(lái)定義 where 子句渴肉,where 子句后跟一個(gè)或者多個(gè)針對(duì)關(guān)聯(lián)類型的約束冗懦,以及一個(gè)或多個(gè)類型參數(shù)和關(guān)聯(lián)類型間的相等關(guān)系爽冕。你可以在函數(shù)體或者類型的大括號(hào)之前添加 where 子句。

檢查兩個(gè) Container 實(shí)例是否包含相同順序的相同元素披蕉,被檢查的兩個(gè) Container 可以不是相同類型的容器(雖然它們可以相同)颈畸,但它們必須擁有相同類型的元素。這個(gè)要求通過(guò)一個(gè)類型約束以及一個(gè) where 子句來(lái)表示:

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
        }
        
        // 檢查每一對(duì)元素是否相等
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }
        
        // 所有元素都匹配没讲,返回 true
        return true
}

這個(gè)函數(shù)的類型參數(shù)列表定義了對(duì)兩個(gè)類型參數(shù)的要求:

  • C1 必須符合 Container 協(xié)議(寫(xiě)作 C1: Container)眯娱。
  • C2 必須符合 Container 協(xié)議(寫(xiě)作 C2: Container)。
  • C1ItemType 必須和 C2ItemType類型相同(寫(xiě)作 C1.ItemType == C2.ItemType)爬凑。
  • C1ItemType 必須符合 Equatable 協(xié)議(寫(xiě)作 C1.ItemType: Equatable)徙缴。

使用:

var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")

var arrayOfStrings = ["uno", "dos", "tres"]

if allItemsMatch(stackOfStrings, arrayOfStrings) {
    print("All items match.")
} else {
    print("Not all items match.")
}
// 打印 “All items match.”

具有泛型 where 子句的擴(kuò)展

你也可以使用泛型 where 子句作為擴(kuò)展的一部分∴倚牛基于以前的例子于样,下面的示例擴(kuò)展了泛型 Stack 結(jié)構(gòu)體,添加一個(gè) isTop(_:) 方法潘靖。

extension Stack where Element: Equatable {
    func isTop(_ item: Element) -> Bool {
        //檢查這個(gè)棧是不是空的
        guard let topItem = items.last else {
            return false
        }
        return topItem == item
    }
}

如果嘗試在其元素不符合 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é)議卦溢『啵基于以前的示例,下面的示例擴(kuò)展了 Container 協(xié)議单寂,添加一個(gè) startsWith(_:) 方法贬芥。

extension Container where Item: Equatable {
    func startsWith(_ item: Item) -> Bool {
        return count >= 1 && self[0] == item
    }
}
if [9, 9, 9].startsWith(42) {
    print("Starts with 42.")
} else {
    print("Starts with something else.")
}
// 打印 "Starts with something else."

上述示例中的泛型 where 子句要求 Item 符合協(xié)議,但也可以編寫(xiě)一個(gè)泛型 where 子句去要求 Item 為特定類型宣决。例如:

extension Container where Item == Double {
    func average() -> Double {
        var sum = 0.0
        for index in 0..<count {
            sum += self[index]
        }
        return sum / Double(count)
    }
}
print([1260.0, 1200.0, 98.6, 37.0].average())
// 打印 "648.9"

具有泛型 where 子句的關(guān)聯(lián)類型

你可以在關(guān)聯(lián)類型后面加上具有泛型 where 的字句蘸劈。例如,建立一個(gè)包含迭代器(Iterator)的容器疲扎,就像是標(biāo)準(zhǔn)庫(kù)中使用的 Sequence 協(xié)議那樣昵时。你應(yīng)該這么寫(xiě):

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
}

迭代器(Iterator)的泛型 where 子句要求:無(wú)論迭代器是什么類型捷雕,迭代器中的元素類型,必須和容器項(xiàng)目的類型保持一致壹甥。makeIterator() 則提供了容器的迭代器的訪問(wèn)接口救巷。

一個(gè)協(xié)議繼承了另一個(gè)協(xié)議,你通過(guò)在協(xié)議聲明的時(shí)候句柠,包含泛型 where 子句浦译,來(lái)添加了一個(gè)約束到被繼承協(xié)議的關(guān)聯(lián)類型。例如溯职,下面的代碼聲明了一個(gè) ComparableContainer 協(xié)議精盅,它要求所有的 Item 必須是 Comparable 的。

protocol ComparableContainer: Container where Item: Comparable { }

泛型下標(biāo)

下標(biāo)能夠是泛型的谜酒,他們能夠包含泛型 where 子句叹俏。你可以把占位符類型的名稱寫(xiě)在 subscript 后面的尖括號(hào)里,在下標(biāo)代碼體開(kāi)始的標(biāo)志的花括號(hào)之前寫(xiě)下泛型 where 子句僻族。例如:

extension Container {
    subscript<Indices: Sequence>(indices: Indices) -> [Item]
        where Indices.Iterator.Element == Int {
            var result = [Item]()
            for index in indices {
                result.append(self[index])
            }
            return result
    }
}

這個(gè) Container 協(xié)議的擴(kuò)展添加了一個(gè)下標(biāo)方法粘驰,接收一個(gè)索引的集合,返回每一個(gè)索引所在的值的數(shù)組述么。這個(gè)泛型下標(biāo)的約束如下:

這個(gè) Container 協(xié)議的擴(kuò)展添加了一個(gè)下標(biāo):下標(biāo)是一個(gè)序列的索引蝌数,返回的則是索引所在的項(xiàng)目的值所構(gòu)成的數(shù)組。這個(gè)泛型下標(biāo)的約束如下:

  • 在尖括號(hào)中的泛型參數(shù) Indices度秘,必須是符合標(biāo)準(zhǔn)庫(kù)中的 Sequence 協(xié)議的類型顶伞。
  • 下標(biāo)使用的單一的參數(shù),indices剑梳,必須是 Indices 的實(shí)例唆貌。
  • 泛型 where 子句要求 Sequence(Indices)的迭代器,其所有的元素都是 Int 類型阻荒。這樣就能確保在序列(Sequence)中的索引和容器(Container)里面的索引類型是一致的挠锥。

綜合一下,這些約束意味著侨赡,傳入到 indices 下標(biāo)蓖租,是一個(gè)整型的序列。
(譯者:原來(lái)的 Container 協(xié)議羊壹,subscript 必須是 Int 型的蓖宦,擴(kuò)展中新的 subscript,允許下標(biāo)是一個(gè)的序列油猫,而非單一的值稠茂。)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子睬关,更是在濱河造成了極大的恐慌诱担,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件电爹,死亡現(xiàn)場(chǎng)離奇詭異蔫仙,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)丐箩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)摇邦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人屎勘,你說(shuō)我怎么就攤上這事施籍。” “怎么了概漱?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵丑慎,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我犀概,道長(zhǎng)立哑,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任姻灶,我火速辦了婚禮,結(jié)果婚禮上诈茧,老公的妹妹穿的比我還像新娘产喉。我一直安慰自己,他們只是感情好敢会,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布曾沈。 她就那樣靜靜地躺著,像睡著了一般鸥昏。 火紅的嫁衣襯著肌膚如雪塞俱。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天吏垮,我揣著相機(jī)與錄音障涯,去河邊找鬼膳汪。 笑死唯蝶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的遗嗽。 我是一名探鬼主播粘我,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼痹换!你這毒婦竟也來(lái)了征字?” 一聲冷哼從身側(cè)響起都弹,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎匙姜,沒(méi)想到半個(gè)月后缔杉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡搁料,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年或详,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片郭计。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡霸琴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出昭伸,到底是詐尸還是另有隱情梧乘,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布庐杨,位于F島的核電站选调,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏灵份。R本人自食惡果不足惜仁堪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望填渠。 院中可真熱鬧弦聂,春花似錦、人聲如沸氛什。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)枪眉。三九已至捺檬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贸铜,已是汗流浹背堡纬。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留萨脑,地道東北人隐轩。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像渤早,于是被迫代替她去往敵國(guó)和親职车。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 泛型(Generics) 泛型代碼允許你定義適用于任何類型的,符合你設(shè)置的要求的,靈活且可重用的 函數(shù)和類型。泛型...
    果啤閱讀 663評(píng)論 0 0
  • 本章將會(huì)介紹 泛型所解決的問(wèn)題泛型函數(shù)類型參數(shù)命名類型參數(shù)泛型類型擴(kuò)展一個(gè)泛型類型類型約束關(guān)聯(lián)類型泛型 Where...
    寒橋閱讀 629評(píng)論 0 2
  • 136.泛型 泛型代碼讓你可以寫(xiě)出靈活,可重用的函數(shù)和類型,它們可以使用任何類型,受你定義的需求的約束悴灵。你可以寫(xiě)出...
    無(wú)灃閱讀 1,452評(píng)論 0 4
  • 【Swift 3.1】23 - 泛型 (Generics) 自從蘋(píng)果2014年發(fā)布Swift扛芽,到現(xiàn)在已經(jīng)兩年多了,...
    Lebron_James閱讀 1,073評(píng)論 2 0
  • 在馬德里旅行時(shí)候遇到的溫州小哥积瞒,說(shuō)他們家好多人分布在歐洲的好幾個(gè)國(guó)家川尖,在歐洲的生活就一個(gè)字形容:幸福~他特別提到了...
    miyocat閱讀 1,331評(píng)論 7 8