位置:首頁 > 高級語言 > Swift教學 > Swift關聯類型

Swift關聯類型

關聯類型

當定義一個協議時,有的時候聲明一個或多個關聯類型作為協議定義的一部分是非常有用的。一個關聯類型給定作用於協議部分的類型一個節點名(或彆名)。作用於關聯類型上實際類型是不需要指定的,直到該協議接受。關聯類型被指定為typealias關鍵字。

關聯類型行為

這裡是一個Container協議的例子,定義了一個ItemType關聯類型:

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

Container協議定義了三個任何容器必須支持的兼容要求:

  • 必須可能通過append方法添加一個新item到容器裡;
  • 必須可能通過使用count屬性獲取容器裡items的數量,並返回一個Int值;
  • 必須可能通過容器的Int索引值下標可以檢索到每一個item。

這個協議冇有指定容器裡item是如何存儲的或何種類型是允許的。這個協議隻指定三個任何遵循Container類型所必須支持的功能點。一個遵循的類型也可以提供其他額外的功能,隻要滿足這三個條件。

任何遵循Container協議的類型必須指定存儲在其裡麵的值類型,必須保證隻有正確類型的items可以加進容器裡,必須明確可以通過其下標返回item類型。

為了定義這三個條件,Container協議需要一個方法指定容器裡的元素將會保留,而不需要知道特定容器的類型。Container協議需要指定任何通過append方法添加到容器裡的值和容器裡元素是相同類型,並且通過容器下標返回的容器元素類型的值的類型是相同類型。

為了達到此目的,Container協議聲明了一個ItemType的關聯類型,寫作typealias ItemType。這個協議不會定義ItemType是什麼的彆名,這個信息留給了任何遵循協議的類型來提供。儘管如此,ItemType彆名支持一種方法識彆在一個容器裡的items類型,以及定義一種使用在append方法和下標中的類型,以便保證任何期望的Container的行為是強製性的。

這裡是一個早前IntStack類型的非泛型版本,適用於遵循Container協議:

struct IntStack: Container {
    // original IntStack implementation
    var items = Int[]()
    mutating func push(item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
    // conformance to the Container protocol
    typealias ItemType = Int
    mutating func append(item: Int) {
        self.push(item)
    }
    var count: Int {
    return items.count
    }
    subscript(i: Int) -> Int {
        return items[i]
    }
}

IntStack類型實現了Container協議的所有三個要求,在IntStack類型的每個包含部分的功能都滿足這些要求。

此外,IntStack指定了Container的實現,適用的ItemType被用作Int類型。對於這個Container協議實現而言,定義 typealias ItemType = Int,將抽象的ItemType類型轉換為具體的Int類型。

感謝Swift類型參考,你不用在IntStack定義部分聲明一個具體的IntItemType。由於IntStack遵循Container協議的所有要求,隻要通過簡單的查找append方法的item參數類型和下標返回的類型,Swift就可以推斷出合適的ItemType來使用。確實,如果上麵的代碼中你刪除了typealias ItemType = Int這一行,一切仍舊可以工作,因為它清楚的知道ItemType使用的是何種類型。

你也可以生成遵循Container協議的泛型Stack類型:

struct Stack<T>: Container {
    // original Stack<T> implementation
    var items = T[]()
    mutating func push(item: T) {
        items.append(item)
    }
    mutating func pop() -> T {
        return items.removeLast()
    }
    // conformance to the Container protocol
    mutating func append(item: T) {
        self.push(item)
    }
    var count: Int {
    return items.count
    }
    subscript(i: Int) -> T {
        return items[i]
    }
}

這個時候,占位類型參數T被用作append方法的item參數和下標的返回類型。Swift 因此可以推斷出被用作這個特定容器的ItemTypeT的合適類型。

擴展一個存在的類型為一指定關聯類型

使用擴展來添加協議兼容性中有描述擴展一個存在的類型添加遵循一個協議。這個類型包含一個關聯類型的協議。

Swift的Array已經提供append方法,一個count屬性和通過下標來查找一個自己的元素。這三個功能都達到Container協議的要求。也就意味著你可以擴展Array去遵循Container協議,隻要通過簡單聲明Array適用於該協議而已。如何實踐這樣一個空擴展,在使用擴展來聲明協議的采納中有描述這樣一個實現一個空擴展的行為:

extension Array: Container {}

如同上麵的泛型Stack類型一樣,Array的append方法和下標保證Swift可以推斷出ItemType所使用的適用的類型。定義了這個擴展後,你可以將任何Array當作Container來使用。