在子子类中覆盖协议默认实现不参与动态分派

Ole*_*ran 10 protocols swift

考虑以下游乐场:

protocol A {
    func f() -> String
}

extension A {
    func f() -> String { return "AAAA" }
}

class B: A {}

class C: B {
    func f() -> String { return "CCCC" }
}

let a: A = C()
let b: B = C()
let c: C = C()

a.f() // "AAAA" - why?
b.f() // "AAAA" - why?
c.f() // "CCCC"
Run Code Online (Sandbox Code Playgroud)

我不明白为什么a.f()b.f()返回"AAAA"- 它们应该返回,"CCCC"因为func f() -> String应该动态调度(因为它在协议中声明).

如果我改成class B这样:

class B: A {
    func f() -> String { return "BBBB" }
}
Run Code Online (Sandbox Code Playgroud)

然后所有三个调用按预期.f()返回"CCCC".

我觉得这是Swift编译器中的一个错误,我在Xcode 7.3.1和8.0-beta3中检查过,这种行为在两者中都是可重现的.

这实际上是预期的行为吗?

Luc*_*tti 4

这里涉及到几条规则。

有时使用静态调度(在这种情况下,我们必须查看 var/let 的类型以找出将使用的实现)。

其他时候则使用动态调度(这意味着使用变量内部对象的实现)。

让我们考虑一下一般的例子

let foo: SomeType1 = SomeType2()
foo.f()
Run Code Online (Sandbox Code Playgroud)

我将使用以下定义

  • classic implementation of f()指示 f() 何时在协议扩展之外定义(因此在结构/类内部)。

  • default implementation of f()指示何时f()在协议扩展内定义。

动态调度

如果SomeType1是一个struct/class有它自己的“经典”实现,f()那么应用多态性

这意味着 ifSomeType2没有f()then的经典实现SomeType1.f()。否则SomeType2.f()获胜。

静态调度

如果SomeType1没有 的经典实现,f()但有默认实现,则多态性被关闭

在这种情况下,默认实现获胜类型let/var

A。

让我们看看你的第一个例子

let a: A = C()
a.f() // "AAAA"
Run Code Online (Sandbox Code Playgroud)

在此 A 没有自己的经典实现(因为它不是结构/类),但有一个默认实现。所以多态性被关闭并被A.f()使用。

b.

第二个例子的规则相同

let b: B = C()
b.f() // "AAAA"
Run Code Online (Sandbox Code Playgroud)

B没有 f() 的经典实现,但有f(). 因此,多态性被关闭并B.f()使用(来自协议扩展)。

C。

最后, type 的对象C位于 type 的常量内C

var c:C
c.f() // "CCCC"
Run Code Online (Sandbox Code Playgroud)

这里C有一个经典的实现f()。在这种情况下,协议实现将被忽略并被C.f()使用。

更多的

让我们看另一个例子

protocol Alpha { }
extension Alpha { func f() -> String { return "Alpha"} }
protocol Beta { }
extension Beta { func f() -> String { return "Beta"} }

class Foo: Alpha, Beta { }

let alpha: Alpha = Foo()
alpha.f() // "Alpha"

let beta: Beta = Foo()
beta.f() // "Beta"
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,包含该值的常量类型再次获胜。如果将Foo对象放入常量中,Foo则会出现编译错误

let foo: Foo = Foo()
foo.f() //

error: ambiguous use of 'f()'
foo.f()
^
Swift 2.playground:2:23: note: found this candidate
extension Beta { func f() -> String { return "Beta"} }
                      ^
Swift 2.playground:6:24: note: found this candidate
extension Alpha { func f() -> String { return "Alpha"} }
Run Code Online (Sandbox Code Playgroud)

  • Joe Groff(Swift 编译器作者之一)评论了这个问题:https://twitter.com/jckarter/status/766739014237892608 (3认同)