Associated Types

When defining a protocol, it is sometimes useful to declare one or more associated types as part of the protocol's definition. An associated type gives a placeholder name (or alias) to a type that is used as part of the protocol. The actual type to use for that associated type is not specified until the protocol is adopted. Associated types are specified with the typealias keyword.

Associated Types in Action

Here's an example of a protocol called Container, which declares an associated type called ItemType:

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

The Container protocol defines three required capabilities that any container must provide:

  • It must be possible to add a new item to the container with an `append(_:) method
  • It must be possible to access a count of the items in the container through a count property that returns an Int value.
  • It must be possible to retrieve each item in the container with a subscript that takes an Int index value.

This protocol doesn't specify how the items in the container should be stored or what type they are allowed to be. The protocol only specifies the three bits of functionality that any type must provide in order to be considered a Container. A conforming type can provide additional functionality, as long as it satisfies these three requirements.

Any type that conforms to the Container protocol must be able to specify the type of values it stores. Specifically, it must ensure that only items of the right type are added to the container, and it must be clear about the type of the items returned by its subscript.

To define these requirements, the Container protocol needs a way to refer to the type of the elements that a container will hold, without knowing what that type is for a specific container. The Container protocol needs to specify that any value passed to the append(_:) method must have the same type as the container's element type, and that the value returned by the container's subscript will be of the same type as the container's element type.

To achieve this, the Container protocol declares an associated type called ItemType, written as typealias ItemType. The protocol does not define what ItemType is an alias for - that information is left for any conforming type to provide. Nontheless, the ItemType alias provides a way to refer to the type of the items in a Container, and to define a type for use with the append(_:) method and subscript, to ensure that the expected behavior of any Container is enforced.

Here's a version of the non-generic IntStack type from earlier, adapted to conform to the Container protocol:

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

The IntStack type implements all three of the Container protocol's requirements, and in each case wraps part of the IntStack type's existing functionality to satisfy these requirements.

Moreover, IntStack specifies that for this implementation of Container, the appropriate ItemType to use is a type of Int. The definition of typealias ItemType = Int turns the abstract type of ItemType into a concrete type of Int for this implementation of the Container protocol.

Thanks to Swift's type inference, you don't actually need to declare a concrete ItemType of Int as part of the definition of IntStack. Because IntStack conforms to all of the requirements of the Container protocol, Swift can infer the appropriate ItemType to use, simply by looking at the type of the append(_:) method's item parameter and the return type of the subscript. Indeed, if you delete the typealias ItemType = Int line from the code above, everything still works, because it is clear what type should be used for ItemType.

You can also make the generic Stack type conform to the Container protocol:

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

This time, the type parameter Element is used as the type of the append(_:) method'sitemparameter and the return type of the subscript. Swift can therefore infer thatElementis the appropriate type to use as theItemType` for this particular container.

Extending an Existing Type to Specify an Associated Type

You can extend an existing type to add conformance to a protocol. This includes a protocol with an associated type.

Swift's Array type already provides an append(_:) method, and a subscript with an Int index to retrieve its elements. These three capabilities match the requirements of the Container protocol. This means that you can extend Array to conform to the Container protocol simply by declaring that Array adopts the protocol. You do this with an empty extension.
extension Array: Container { }

Array's existing append(_:) method and subscript enable Swift to infer the appropriate type to use for ItemType, just as for the generic Stack type above. After defining this extension, you can use any Array as a Container.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末搂抒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子焰雕,更是在濱河造成了極大的恐慌芳杏,老刑警劉巖爵赵,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件亚再,死亡現(xiàn)場離奇詭異氛悬,居然都是意外死亡耘柱,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門镜遣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悲关,“玉大人娄柳,你說我怎么就攤上這事★ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵航夺,是天一觀的道長崔涂。 經(jīng)常有香客問我,道長锚烦,這世上最難降的妖魔是什么帝雇? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任尸闸,我火速辦了婚禮吮廉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宦芦。我一直安慰自己调卑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布注益。 她就那樣靜靜地躺著丑搔,像睡著了一般啤月。 火紅的嫁衣襯著肌膚如雪碳锈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天强重,我揣著相機(jī)與錄音间景,去河邊找鬼。 笑死圾亏,一個胖子當(dāng)著我的面吹牛封拧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播曹铃,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼陕见,長吁一口氣:“原來是場噩夢啊……” “哼评甜!你這毒婦竟也來了仔涩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤承匣,失蹤者是張志新(化名)和其女友劉穎锤悄,沒想到半個月后嘉抒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體些侍,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蚂会,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年胁住,在試婚紗的時候發(fā)現(xiàn)自己被綠了趁猴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片儡司。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖余指,靈堂內(nèi)的尸體忽然破棺而出捕犬,到底是詐尸還是另有隱情,我是刑警寧澤酵镜,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布碉碉,位于F島的核電站,受9級特大地震影響淮韭,放射性物質(zhì)發(fā)生泄漏垢粮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一缸濒、第九天 我趴在偏房一處隱蔽的房頂上張望足丢。 院中可真熱鬧,春花似錦庇配、人聲如沸斩跌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽袖订。三九已至洛姑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間龄广,已是汗流浹背两入。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人虹曙。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓疏哗,卻偏偏與公主長得像返奉,于是被迫代替她去往敵國和親弦讽。 傳聞我的和親對象是個殘疾皇子被碗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容