泛型基礎知識

泛型是型別的佔位符,允許你編寫可以跨多種型別應用的靈活程式碼。使用泛型優於 Any的優點是它們仍然允許編譯器強制實現強型別安全性。

通用佔位符在尖括號 <> 中定義。

通用函式

對於函式 ,此佔位符位於函式名稱後面:

/// Picks one of the inputs at random, and returns it
func pickRandom<T>(_ a:T, _ b:T) -> T {
    return arc4random_uniform(2) == 0 ? a : b
}

在這種情況下,通用佔位符是 T。當你來呼叫函式時,Swift 可以為你推斷 T 的型別(因為它只是作為實際型別的佔位符)。

let randomOutput = pickRandom(5, 7) // returns an Int (that's either 5 or 7)

這裡我們將兩個整數傳遞給函式。因此 Swift 推斷 T == Int - 因此函式簽名被推斷為 (Int, Int) -> Int

由於泛型提供了強大的型別安全性 - 函式的引數和返回必須是相同的型別。因此以下內容不會編譯:

struct Foo {}

let foo = Foo()

let randomOutput = pickRandom(foo, 5) // error: cannot convert value of type 'Int' to expected argument type 'Foo'

通用型別

為了將泛型與結構列舉一起使用 ,可以在型別名稱後定義通用佔位符。

class Bar<T> {
    var baz : T
    
    init(baz:T) {
        self.baz = baz
    }
}

當你使用 Bar 類時,此通用佔位符將需要一個型別。在這種情況下,可以從初始化 init(baz:T) 推斷出。

let bar = Bar(baz: "a string") // bar's type is Bar<String>

這裡通用佔位符 T 被推斷為 String 型別,因此建立了 Bar<String> 例項。你還可以明確指定型別:

let bar = Bar<String>(baz: "a string")

與型別一起使用時,給定的通用佔位符將在給定例項的整個生命週期內保持其型別,並且在初始化後不能更改。因此,當你訪問屬性 baz 時,對於此給定例項,它將始終為 String 型別。

let str = bar.baz // of type String

傳遞通用型別

當你來傳遞泛型型別時,在大多數情況下,你必須明確你期望的通用佔位符型別。例如,作為函式輸入:

func takeABarInt(bar:Bar<Int>) {
    ...
}

此功能只接受 Bar<Int>。嘗試傳入通用佔位符型別不是 IntBar 例項將導致編譯器錯誤。

通用佔位符命名

通用佔位符名稱不僅限於單個字母。如果給定的佔位符表示有意義的概念,則應為其指定一個描述性名稱。例如,Swift 的 Array 有一個名為 Element 的通用佔位符,它定義了給定 Array 例項的元素型別。

public struct Array<Element> : RandomAccessCollection, MutableCollection {
    ...
}