決定 associated type 型別的六種方法

protocol 可以透過 associated type 宣告型別代號,而代號的真正型別則可從遵從 protocol 的型別,protocol 的 extension,遵從 protocol 的型別的 extension,在宣告 associatedtype 時給予初始值等六種方法決定。

1 由遵從 protocol 的型別推理。

protocol Princess {
associatedtype HandyMan
var myHandyMan: HandyMan { get }
}

struct TaiwanPrince {
func buyFood() {
print("買珍珠奶茶")
}
}

struct TaiwanPrincess: Princess {
var myHandyMan = TaiwanPrince()
func buyFoodForMe() {
myHandyMan.buyFood()
}
}

let snowWhite = TaiwanPrincess()
snowWhite.buyFoodForMe()

由於 struct TaiwanPrincess 定義 myHandyMan 的型別為 TaiwanPrince,所以 associatedtype HandyMan 將聰明地被判斷為 TaiwanPrince。

2 由 protocol extension 的定義推理。

extension Princess {
var myHandyMan: TaiwanPrince {
return TaiwanPrince()
}
}

struct TaiwanPrincess: Princess {
func buyFoodForMe() {
myHandyMan.buyFood()
}
}

let snowWhite = TaiwanPrincess()
snowWhite.buyFoodForMe()

因為 extension Princess 將 myHandyMan 宣告為型別 TaiwanPrince,因此 associatedtype HandyMan 將聰明地被判斷為 TaiwanPrince。

3 在遵從 protocol 的型別裡以 typealias 指定。

struct TaiwanPrincess: Princess {
typealias HandyMan = TaiwanPrince
var myHandyMan: HandyMan
}
let handsomePrince = TaiwanPrince()
let snowWhite = TaiwanPrincess(myHandyMan: handsomePrince)
snowWhite.myHandyMan.buyFood()

typealias HandyMan = TaiwanPrince 得知 HandyMan 的型別為 TaiwanPrince。

4 在遵從 protocol 的型別的 extension 裡指定。

protocol Princess {
associatedtype HandyMan
func buyFoodForMe(myHandyMan: HandyMan)
}

struct TaiwanPrince {
func buyFood() {
print("買珍珠奶茶")
}
}

struct TaiwanPrincess {
}

extension TaiwanPrincess: Princess {
typealias HandyMan = TaiwanPrince
func buyFoodForMe(myHandyMan: TaiwanPrince) {
myHandyMan.buyFood()
}
}

let snowWhite = TaiwanPrincess()
snowWhite.buyFoodForMe(myHandyMan: TaiwanPrince())

HandyMan 的型別為 TaiwanPrince,由於可從 function buyFoodForMe 的參數型別推理,所以 typealias HandyMan = TaiwanPrince 也可以拿掉。

5 由遵從 protocol 的型別的 generic 型別代號決定。

此情況有以下兩種 case:

(1) 由遵從 protocol 的型別推理出 generic 型別代號將等於 associated type。

protocol Princess {
associatedtype HandyMan
var myHandyMan: HandyMan { get }
}

struct TaiwanPrincess<T>: Princess {
var myHandyMan: T
}

let taiwanPrincess = TaiwanPrincess(myHandyMan: "台灣的王子")
print(taiwanPrincess.myHandyMan)

生成 TaiwanPrincess 時,參數 myHandyMan 傳入字串 “台灣的王子”, T 的型別將變成 String,也就是 myHandMan 的型別是 String,因此推理出 associatedtype HandyMan 的型別為 String。

(2) 遵從 protocol 的型別的 generic 型別代號和 associatedtype 有著一樣的名字。

protocol Princess {
associatedtype HandyMan
func buyFoodForMe(myHandMan: HandyMan)
}

struct TaiwanPrincess<HandyMan> {
func buyIphoneForMe(myHandMan: HandyMan) {
}
}

extension TaiwanPrincess: Princess {
func buyFoodForMe(myHandMan: HandyMan) {
}
}

let snowWhite = TaiwanPrincess<String>()
snowWhite.buyFoodForMe(myHandMan: "台灣的王子")
snowWhite.buyIphoneForMe(myHandMan: "台灣的王子")

protocol Princess 的 associatedtype 叫 HandyMan,struct TaiwanPrincess 的 generic 型別代號也叫 HandyMan,因此當我們用 extension 讓 TaiwanPrincess 遵從 protocol Princess 時,將讓 TaiwanPrincess 的型別代號等同於 Princess 的 associatedtype,因此 TaiwanPrincess<String>() 將讓 associatedtype 變成 String。

6 宣告 associatedtype 時即給預設值。

protocol Princess {
associatedtype HandyMan = String
func buyFoodForMe(myHandMan: HandyMan)
}

struct TaiwanPrincess: Princess {
func buyFoodForMe(myHandMan: String) {
print("\(myHandMan)買消夜給公主")
}
}

let snowWhite = TaiwanPrincess()
snowWhite.buyFoodForMe(myHandMan: "台灣的王子")

由於 associatedtype HandyMan = String,所以 HandyMan 將變成 String。

不過我們也可以修改 protocol 裡設定的預設值,例如以下例子:

protocol Princess {
associatedtype HandyMan = String
func buyFoodForMe(myHandMan: HandyMan)
}

struct TaiwanPrince {
var name: String
}

struct TaiwanPrincess: Princess {
typealias HandyMan = TaiwanPrince
func buyFoodForMe(myHandMan: TaiwanPrince) {
print("\(myHandMan.name)買消夜給公主")
}
}

let snowWhite = TaiwanPrincess()
let handsomePrince = TaiwanPrince(name: "彼得潘")
snowWhite.buyFoodForMe(myHandMan: handsomePrince)

associatedtype HandyMan 變成 TaiwanPrince。由於可從 function buyFoodForMe 的參數型別推理,所以 typealias HandyMan = TaiwanPrince 也可以拿掉。

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com