protocolを利用する上で
protocol 内にassociatedTypeやSelfがあった場合、
例:
protocol Caffeteria {
associatedType Caffe
var name: String { get }
func place(_ cafe: Caffe )
}
class StarBucks: Caffeteria {
typealias Caffe = String
var name = "StarBucks"
func place(_ cafe: String) { "StarBucks is at Shinjuku" }
}
let starBucks: Caffeteria = StarBucks()
// コンパイルエラーが出る。
なぜなら、starBucksを生成する際に、プロトコルであるCaffeteriaを型として利用しているが、型の中のassociatedTypeが何に準拠しているかその時点でわからない。(Selfの場合も同様)。
TypeErasure
なので、別のクラスを生成してあげて、associatedTypeが何の準拠しているのかを伝えてあげる。
class AnyCaffeteria: Caffeteria {
let name: String
let _place : (Caffe) -> Void?
init <X: Caffeteria >(_ base: X) where X.Caffe = Caffe {
name = base.name
_place = base.place
}
func place(_ cafe: String) {
_place(cafe)
}
}
let starBucks: AnyCaffeteria = AnyCaffeteria(StarBucks())
AnyCaffeteriaを生成する際に、StarBucksのクラスをbase入れてあげる。そこで初めてassociatedTypeの型がわかるようにinit時にwhereを用いている。イメージ:whereは超静的に型をみてくれている。
Anyを用いる
上記の件に対して、anyを使うことで簡単に記述が可能になる。
protocol Caffeteria {
associatedtype Caffe
var name: String { get }
func place(_ cafe: Caffe)
}
class StarBucks: Caffeteria {
typealias Caffe = String
var name = "Starbucks"
func place(_ cafe: String) { "Starbucks is at Shinjuku" }
}
let starbucks: any Caffeteria = StarBucks()
any Caffeteriaのように記述することで、静的に出なく動的に判断してくれる!なので、コンパイルが通る!
正確には動的Dispatchを行なってくれている。
注意点
Existential Containerを用いて、メモリを多く取ってしまうため、パフォーマンスがあまり良くない。
イメージ:
何人誕生日パーティーに集まるかわからないので、とりあえずとても大きいケーキを用意しておく!