为什么以下代码在#2处打印“BaseP”?
protocol BaseP { func foo() }
extension BaseP { func foo() { print("BaseP") } }
protocol SubP: BaseP {}
extension SubP { func foo() { print("SubP") } }
class C: SubP {}
let subP1: SubP = C()
subP1.foo() // #1 prints "SubP", fine.
class BaseC: BaseP {}
class SubC: BaseC, SubP {}
let subP2: SubP = SubC()
subP2.foo() // #2 prints "BaseP". why?
在这两种情况下,我们在静态类型为foo()
的引用上调用SubP
,引用具有符合
SubP
的类的动态类型的对象。即使是静态调度,我也认为它仍应调用
SubP.foo()
。为什么在第2个调用基本协议(protocol)实现?
另外,为什么从
BaseC
继承会有所作为(如果在
class SubC
行中,我删除了BaseC
或将其替换为BaseP
,那么它突然打印出“SubP”)?
最佳答案
调用subP1.foo()
或subP2.foo()
时,就是在foo
中调用满足协议(protocol)要求foo
的BaseP
。 (即见证BaseP.foo
的方法)。 只能有一个这样的见证人。
另一个重要的事情是foo
要求不是在SubP
中,而是在BaseP
中。 SubP
的唯一要求是协调者还必须符合BaseP
。
对于subP1
,C
直接符合SubP
。为了解决SubP
的唯一要求,C
也必须符合BaseP
。现在,编译器需要确定哪种方法可以见证foo
。有两种方法可用,但SubP
扩展名中的一种隐藏了BaseP
扩展名中的一种,因此选择了SubP
扩展名中的一种作为见证,因此您看到了SubP
打印。
对于subP2
,当您使foo
与BaseC
一致时,就已经确定BaseP
的见证人。在这里,只有一种选择-foo
扩展名中的BaseC
。当您稍后将SubC
与SubP
兼容时,剩下的唯一要求是SubC
也应符合BaseP
。好吧,您说SubC
继承自BaseC
,所以很好。编译器很高兴,foo
扩展名中的BaseP
最终成为见证人。