天天看點

對照Java學習Swift--泛型(Generics)

簡介

泛型代碼讓你能夠根據自定義的需求,編寫出适用于任意類型、靈活可重用的函數及類型。它能讓你避免代碼的重複,用一種清晰和抽象的方式來表達代碼的意圖。從Java1.5開始,引進了泛型,Swift和Java的泛型很類似,都很強大,學過Java的同學都知道。

Swift中的數組、字典、集合都是泛型集合,同樣的Java的集合類型也是支援泛型的,可能是為了相容,也可以不指定泛型。

泛型類型

Swift 允許你定義泛型類型。這些自定義類、結構體和枚舉可以适用于任何類型。

我們看下自定義棧類型的資料結構:

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

類型限制

你可以在一個類型參數名後面放置一個類名或者協定名,并用冒号進行分隔,來定義類型限制,它們将成為類型參數清單的一部分。

看個例子:

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

任何 Equatable 類型都可以安全地使用在 findIndex(::) 函數中,因為其保證支援等式操作符。在這個函數中,定義一個 Equatable 類型限制作為類型參數定義的一部分。

關聯類型

定義一個協定時,有的時候聲明一個或多個關聯類型作為協定定義的一部分将會非常有用。關聯類型為協定中的某個類型提供了一個占位名(或者說别名),其代表的實際類型在協定被采納時才會被指定。你可以通過 associatedtype 關鍵字來指定關聯類型。

下面例子定義了一個 Container 協定,該協定定義了一個關聯類型 ItemType:

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

讓泛型 Stack 結構體遵從 Container 協定:

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]
    }
}
           

這一次,占位類型參數 Element 被用作 append(_:) 方法的 item 參數和下标的傳回類型。Swift 可以據此推斷出 Element 的類型即是 ItemType 的類型。

Where 子句

類型限制讓你能夠為泛型函數或泛型類型的類型參數定義一些強制要求。

下面的例子定義了一個名為 allItemsMatch 的泛型函數,用來檢查兩個 Container 執行個體是否包含相同順序的相同元素。如果所有的元素能夠比對,那麼傳回 true,否則傳回 false。

被檢查的兩個 Container 可以不是相同類型的容器(雖然它們可以相同),但它們必須擁有相同類型的元素。這個要求通過一個類型限制以及一個 where 子句來表示:

func allItemsMatch<
    C1: Container, C2: Container
    where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
    (someContainer: C1, _ anotherContainer: C2) -> Bool {

        // 檢查兩個容器含有相同數量的元素
        if someContainer.count != anotherContainer.count {
            return false
        }

        // 檢查每一對元素是否相等
        for i in .<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }

        // 所有元素都比對,傳回 true
        return true
}
           

看了這個例子很容易明白,不用多說。有Java基礎的同學,學起來應該很容易的。