associatedtypeはprotocolのジェネリクスのようなもので、複数の型に対応した定義を書くことができる。
protocol P1 {
associatedtype T
func some(x: T)
func some2(x: T)
}
class C1: P1 {
func some(x: Int) {
print(x)
}
/*
// ambiguous inference of associated type 'T': 'String' vs. 'Int'
func some2(x: String) {
print(x)
}
*/
func some2(x: Int) {
print(x)
}
}
class C2: P1 {
func some(x: Int) {
print(x)
}
// OK
func some2<T>(x: T) {
print(x)
}
}
通常、protocolはexistential typeとして変数の型に指定できるが、 associatedtypeが含まれるとその型が不明なので指定できない。
// protocol 'P1' can only be used as a generic constraint because it has Self or associated type requirements
func f(x: P1) {
}
ジェネリクスの型パラメータの型には指定できる。
func f<T:P1>(x: T, value: T.T) {
x.some(x: value)
}
ということで次のようなクラスでラップすればインタフェースを変えずにこれを共通の型として指定できる。 protocolやこのようなラッパークラスによって元の型の情報を失わせる処理をtype erasureと呼ぶようだ。
class AnyP1<T, U: P1>: P1 where U.T == T {
var base: U
init(_ base: U)
{
self.base = base
}
func some(x: T) {
base.some(x: x)
}
func some2(x: T) {
base.some2(x: x)
}
}
var x: AnyP1 = AnyP1(C1())