Swift 泛型 (Generics)

泛型 (Generics) 讓你能寫出靈活、可重用的函式和型別,它們可以與任何型別一起工作,只要該型別滿足你定義的約束。

這是 Swift 最強大的特性之一。事實上,Swift 標準庫的大部分內容都是用泛型寫的。例如,Array<Element>Dictionary<Key, Value> 都是泛型集合。

泛型函式

假設你要寫一個交換兩個變數值的函式。如果不用泛型,你可能要為 Int 寫一個,為 String 寫一個...這太麻煩了。

使用泛型,只需寫一次:

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}
  • <T>:這是一個佔位符型別名稱 (通常用 T, U, V 等單個大寫字母表示)。它告訴 Swift 「T 是一個型別」,但具體是什麼型別現在不用管,只要 ab 是同一個型別 T 就好。
var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
// someInt 現在是 107

var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString 現在是 "world"

泛型型別

你也可以自定義泛型型別。例如實現一個泛型的 Stack (堆疊):

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

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

型別約束 (Type Constraints)

有時候你不能接受「任何」型別,而是希望該型別必須遵循某個 Protocols 或繼承自某個 Class。

語法:func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U)

例如,要寫一個函式能在陣列中找到某個值的索引,並不是所有型別都能用 == 比較。只有遵循 Equatable 協定的型別才能比較。

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