Skip to content

Instantly share code, notes, and snippets.

@niwatako
Last active April 5, 2023 21:28
Show Gist options
  • Select an option

  • Save niwatako/4011c9f77019e38b85e3 to your computer and use it in GitHub Desktop.

Select an option

Save niwatako/4011c9f77019e38b85e3 to your computer and use it in GitHub Desktop.
【続編】もしも `extension PROTOCOL where Self: T` を親子のクラスにそれぞれ適用したら採用される実装はどっち #CodePiece
// A とそのサブクラス B が、"プロパティnameを持つ" SomeProtocolに準拠することを宣言。
class A {}
class B: A {}
protocol SomeProtocol { var name: String { get } }
extension A: SomeProtocol {}
// Aにnameのデフォルト実装を用意
extension SomeProtocol where Self: A {
var name: String { return "My name is A" }
}
// BはAを継承していますが、更にデフォルト実装を上書きします
extension SomeProtocol where Self: B {
var name: String { return "My name is B" }
}
// この状態では、それぞれ異なる name の実装が機能します
A().name // My name is A
B().name // My name is B
// ところが、インスタンスのプロパティとして格納された途端に...
class C {
var person: SomeProtocol
init(_ person: SomeProtocol) { self.person = person }
}
let cA = C(A())
let cB = C(B())
// Bの name は スーパークラス A のデフォルト実装が実行されるようになりました!
cA.person.name // My name is A
cB.person.name // My name is A
// ただし、Genericsを使うと
class D<T: SomeProtocol>: SomeProtocol {
var person: T
init(_ person: T) { self.person = person }
var name: String { return person.name }
}
// A、Bそれぞれのデフォルト実装が採用されました!
let dA = D<A>(A())
let dB = D<B>(B())
dA.person.name // My name is A
dB.person.name // My name is B
/*
では Generics を使ったオブジェクトが、
再びGenericsを使っていないインスタンスに格納されると...
※もう一度 class C を利用してみます
*/
//class C {
// var person: SomeProtocol
// init(_ person: SomeProtocol) { self.person = person }
//}
let cA2 = C(dA)
let cB2 = C(dB)
cA2.person.name // My name is A
cB2.person.name // My name is A
/**
`extension PROTOCOL where Self: T` が親子に定義された時、
型情報Tが保持された文脈では、Tにextensionしたものが実行されるが、
PLOTOCOL の情報のみの文脈になると、
親クラスに extension されたものが実行される(と思われる)
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment