Swift 子类中协议默认实现的重写不';不参与动态调度

Swift 子类中协议默认实现的重写不';不参与动态调度,swift,protocols,Swift,Protocols,考虑以下问题: 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" -

考虑以下问题:

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"
我不明白为什么
a.f()
b.f()
返回
“AAAA”
-它们应该返回
“CCCC”
,因为
func f()->字符串应该动态调度(正如协议中声明的那样)

如果我将B类更改为如下所示:

class B: A {
    func f() -> String { return "BBBB" }
}
然后,对
.f()
的所有三个调用都会按预期返回
“CCCC”

我觉得这是Swift编译器中的一个bug,我检查了Xcode 7.3.1和8.0-beta3,这两个版本中的行为都是可复制的


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

这里涉及到几个规则

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

其他时候则使用动态分派(这意味着使用变量内对象的实现)

让我们考虑一般的例子

let foo: SomeType1 = SomeType2()
foo.f()
我将使用以下定义

  • f()的经典实现
    指示何时在协议扩展外部(因此在结构/类内部)定义f()

  • f()
    的默认实现,指示何时在协议扩展中定义了
    f()

动态调度 如果
SomeType1
是一个
struct/class
,具有自己的
f()
的“经典”实现,则应用多态性

这意味着如果
SomeType2
没有
f()
的经典实现,则使用
SomeType1.f()
。否则
SomeType2.f()
将获胜

静态调度 如果
SomeType1
没有
f()
的经典实现,但是有一个默认实现,那么多态性将被关闭

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

A. 让我们看一下您的第一个示例

let a: A = C()
a.f() // "AAAA"
在这种情况下,A没有自己的经典实现(因为它不是结构/类),但有一个默认实现。因此,多态性被关闭,并使用
A.f()

B 第二个例子也是这样

let b: B = C()
b.f() // "AAAA"
B
没有f()的经典实现,但有一个默认实现
f()
。因此,多态性被关闭,并使用
B.f()
(来自协议扩展)

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

var c:C
c.f() // "CCCC"
这里
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"
正如您所看到的,同样,包含该值的常量类型获胜。如果将
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"} }

@matt,我不同意这个问题是重复的,因为基本协议将
f()
方法声明为需求,而不是扩展,因此链接的问题不能回答我的问题。请考虑重新打开这个问题。这是一个重复,因为答案回答你的问题。也不要认为这是一个复制品。虽然相似,我也不认为这是一个复制品。有相同的答案并不意味着问题是重复的。例如,“2+1是什么?”和“π的第一个数字是什么?”。不是重复的问题,而是相同的答案。我在协议a中声明了
f()
(即作为一项要求),这将强制进行动态调度(如中所述)。Joe Groff(Swift编译器作者之一)对此问题发表了评论: