swift 泛型

泛型函數

泛型函數可以適用於任何類型:
func swapTwoValues<T>(_ a: inout T, _ b: inout T) { let temporaryA = a
a= b
b = temporaryA
}
func swapTwoInts(_ a: inout Int, _ b: inout Int)
func swapTwoValues<T>(_ a: inout T, _ b: inout T)
這個函數的泛型版本使用了佔位類型名來代替實際類型名。佔位類型名沒有指明T必須是什麼類型,但是它指明瞭a和b必須是同一類型T,無論T代表什麼類型,只有swapTwoValues(_:_:)函數在調用時,才能根據傳入的實際類型決定T所代表的類型。
另外一個不同之處在於這個泛型函數名後面跟着佔位類型名,並用尖括號括起來。這個尖括號告訴swift那個T是swapTwoValues(_:_:)函數定義內的一個佔位類型名,因此swift不會去查找名爲T的實際類型。
var someInt = 3
var anotherInt = 107 swapTwoValues(&someInt, &anotherInt)
// someInt 現在 107, and anotherInt 現在 3
var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString 現在 "world", and anotherString 現在 "hello"

泛型類型

除了泛型函數,swift還允許你定義泛型。這些自定義類、結構體和枚舉可以適用於任何類型,類似於Array和Dicitionary。
這部分內容將向你展示如果編寫一個名爲Stack的泛型集合類型。棧是一系列值的有序集合,和Array類似,但它相比swift的Array類型有更多的操作限制。數組允許在數組的任意位置插入元素或是刪除其中任意位置的元素。而棧只允許在集合的末端添加新的元素。
 struct Stack<Element> {
     var items = [Element]()
     mutating func push(item: Element) {
         items.append(item)
     }
     mutating func pop() -> Element {
         return items.removeLast()
} }
這些方法被標記爲mutating,因爲它們需要修改結構體的Item數組。

擴展一個泛型類型

當你擴展一個泛型類型的時候,並不需要再擴展的定義中提供類型參數列表。原始類型定義彙總聲明的類型參數列表在擴展中可以直接使用,並且這些來自原始類型中的參數名稱會被用作原始定義中類型參數的引用。
 extension Stack {
     var topItem: Element? {
         return items.isEmpty ? nil : items[items.count - 1]
     }
}
計算型屬性現在可以用來訪問任意Stack實例的頂端元素且不移除它:
 if let topItem = stackOfStrings.topItem {
     print("The top item on the stack is \(topItem).")
}

類型約束

下面的函數爲非泛型函數,該函數的功能是在一個String數組中查找給定String值的索引。
 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)")
 }
下面展示的上述的函數的泛型版本
 func findIndex<T>(of valueToFind: T, in array:[T]) -> Int? {
     for (index, value) in array.enumerated() {
         if value == valueToFind {
             return index
} }
return nil }
上面所寫的函數無法通過編譯。問題出在相等性檢查上,不是所有的swift類型都可以用等式符進行比較。不過,所有的這些並不會讓我們無從下手。swift標準庫中定義了一個Equatable協議,該協議要求任何遵循該協議的類型必須實現等式符及不等符,從而能對該類型的任何兩個值進行比較。所有的swift標準類型自動支持Equatable協議。
 func findIndex<T: Equatable>(array: [T], _ valueToFind: T) -> Int? {
     for (index, value) in array.enumerate() {
         if value == valueToFind {
             return index
} }
return nil }
任何符合Equatable協議的類型T都能用這個泛型函數:
let doubleIndex = findIndex(of: 9.3, in: [3.14159, 0.1, 0.25])
// doubleIndex 類型爲 Int?,其值爲 nil,因爲 9.3 不在數組中
let stringIndex = findIndex(of: "Andrea", in: ["Mike", "Malcolm", "Andrea"]) // stringIndex 類型爲 Int?,其值爲 2

關聯類型

下面例子定義了一個Container協議,該協議定義了一個關聯類型:
protocol Container {
    associatedtype ItemType
    mutating func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}
由於swift的類型推斷,實際上不用再Stack的定義中聲明ItemType爲Int。因爲Stack符合Container協議的所有要求,swift只需通過append(_:)方法的item參數類型和下標返回值的類型,就可以推斷出ItemType的具體類型。實際上,如果在不寫typealias ItemType = Int 這一行,一切仍舊可以正常工作,因爲swift清楚的知道ItemType應該是那種類型:
struct Stack<Element>: Container {
// Stack<Element> 的原始實現部分 var items = [Element]()
mutating func push(item: Element) {
         items.append(item)
     }
     mutating func pop() -> Element {
         return items.removeLast()
}
// Container 協議的實現部分
mutating func append(item: Element) {
         self.push(item)
     }
     var count: Int {
         return items.count
     }
     subscript(i: Int) -> Element {
         return items[i]
     }
}

泛型Where語句

爲關聯類型定義約束也是非常有用的。可以在參數列表中通過where子句爲關聯類型定義約束。能通過where子句要求一個關聯類型遵從某個特定的協議,以及某個特定的類型參數和關聯類型必須類型相同。可以通過將where關鍵字跟在類型參數列表後面來定義where子句,where子句後跟一個或者多個針對關聯類型的約束,以及一個或多個類型參數和關聯類型間的相等關係。可以在函數體或者類型的大括號之前添加where子句。
func allItemsMatch<C1: Container, C2: Container>
    (_ someContainer: C1, _ anotherContainer: C2) -> Bool
    where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
// 檢查兩個容器含有相同數量的元素
if someContainer.count != anotherContainer.count {
            return false
        }
// 檢查每一對元素是否相等
for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
} }
// 所有元素都匹配,返回 true return true
}
這個函數接受兩個參數,參數someContainer的類型爲C1,參數anotherContainer的類型爲C2。C1和C2是容器的兩個佔位類型參數,函數被調用時才能確定他們的具體類型。
這個函數的類型參數列表還定義了對兩個類型參數的要求:
C1必須符合Container協議
C2必須符合Container協議
C1的ItemType必須和C2的ItemType類型相同
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.”

取自《the swift programming language》中文版
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章