sub class 中的方法自定义未调用

Method customization in sub class not called

似乎在协议扩展中具有默认实现的协议中定义为自定义点的函数无法在通过基 class 间接继承协议的子 class 中自定义,如果该基地 class 一开始就没有自定义功能。

这是一个简单的协议:

protocol MyProtocol
{
    func myFunc() -> String
}

使用默认实现:

extension MyProtocol
{
    func myFunc() -> String
    {
        return "hello from extension"
    }
}

让我们像这样创建一个基础 class 和一个子 class:

class BaseClass: MyProtocol
{
}

class SubClass: BaseClass
{
    func myFunc() -> String
    {
        return "hello from SubClass"
    }
}

BaseClass().myFunc()                    // "hello from extension"
(BaseClass() as MyProtocol).myFunc()    // "hello from extension"

SubClass().myFunc()                     // "hello from SubClass"
(SubClass() as BaseClass).myFunc()      // "hello from extension"
(SubClass() as MyProtocol).myFunc()     // "hello from extension"

现在在基础中进行了自定义 class:

class BaseClass: MyProtocol
{
    func myFunc() -> String
    {
        return "hello from BaseClass"
    }
}

class SubClass: BaseClass
{
    override func myFunc() -> String
    {
        return "hello from SubClass"
    }
}

BaseClass().myFunc()                    // "hello from BaseClass"
(BaseClass() as MyProtocol).myFunc()    // "hello from BaseClass"

SubClass().myFunc()                     // "hello from SubClass"
(SubClass() as BaseClass).myFunc()      // "hello from SubClass"
(SubClass() as MyProtocol).myFunc()     // "hello from SubClass"

这是预期的行为吗?

编辑 11 月 14 日:

关于这篇文章的注意事项:http://nomothetis.svbtle.com/the-ghost-of-swift-bugs-future from matt评论:

我认为这与我的问题没有严格的关系,因为本文没有涵盖 subclass 间接继承协议的情况(这似乎有所不同)。在后一种情况下,即使函数是自定义点(协议要求的一部分),静态调度也会发生,这对我来说并不明显。根据调用时的推断类型,行为会有所不同。

本文涵盖了另一种情况,其中函数在扩展中具有默认实现,但不是协议要求的一部分,并且可能隐藏子class 自定义。

编辑 11 月 17 日:

可能与 In Swift, why subclass method cannot override the one, provided by protocol extension in superclass

重复

带有方法实现的协议扩展使我们陷入这样一种情况:有时我们是多态的(对象的内部类型很重要)有时我们不是(对象的外部类型化或强制转换方式很重要) .

为了探索这一点,我使用了一个包含以下参数的测试网格:

  • 协议本身是否也需要方法?

  • 采用者是结构还是class?

  • 采纳者是否实施该方法?


1。协议不需要方法

我们从第一个问题的答案是否定的开始。所以,这里是类型声明:

protocol Flier {
}
extension Flier {
    func fly() {
        print("flap flap flap")
    }
}
struct Bird : Flier {
}
struct Insect : Flier {
    func fly() {
        print("whirr")
    }
}
class Rocket : Flier {
    func fly() {
        print("zoooom")
    }
}
class AtlasRocket : Rocket {
    override func fly() {
        print("ZOOOOOM")
    }
}
class Daedalus : Flier {
    // nothing
}
class Icarus : Daedalus {
    func fly() {
        print("fall into the sea")
    }
}

下面是测试:

let b = Bird()
b.fly() // flap flap flap
(b as Flier).fly() // flap flap flap

let i = Insect()
i.fly() // whirr
(i as Flier).fly() // flap flap flap

let r = Rocket()
r.fly() // zoooom
(r as Flier).fly() // flap flap flap

let r2 = AtlasRocket()
r2.fly() // ZOOOOOM
(r2 as Rocket).fly() // ZOOOOOM
(r2 as Flier).fly() // flap flap flap

let d = Daedalus()
d.fly() // flap flap flap
(d as Flier).fly() // flap flap flap

let d2 = Icarus()
d2.fly() // fall into the sea
(d2 as Daedalus).fly() // flap flap flap
(d2 as Flier).fly() // flap flap flap

结果: 对象的类型很重要。实际上,编译器仅从对象的类型化方式知道在哪里寻找将被调用的 fly 实现;所有必要的信息都在编译时出现。一般情况下不需要动态调度。

AtlasRocket 是个例外,它是一个子class,它的超级class 有自己的实现。当 AtlasRocket 被输入为它的 superclass, Rocket 时,它仍然(出于飞行目的)是一个 AtlasRocket。但这并不奇怪,因为这是一个 subclass/superclass 的情况,多态性和动态调度是有效的;显然我们不会仅仅因为 还有 协议扩展就关闭动态调度。


2。该协议确实需要方法

现在第一个问题的答案是肯定的。类型声明与之前完全相同,除了我在所有类型的名称中添加了“2”,并且协议本身包含方法作为要求:

protocol Flier2 {
    func fly() // *
}
extension Flier2 {
    func fly() {
        print("flap flap flap")
    }
}
struct Bird2 : Flier2 {
}
struct Insect2 : Flier2 {
    func fly() {
        print("whirr")
    }
}
class Rocket2 : Flier2 {
    func fly() {
        print("zoooom")
    }
}
class AtlasRocket2 : Rocket2 {
    override func fly() {
        print("ZOOOOOM")
    }
}
class Daedalus2 : Flier2 {
    // nothing
}
class Icarus2 : Daedalus2 {
    func fly() {
        print("fall into the sea")
    }
}

这是测试;它们是相同的测试,所有类型的名称都添加了“2”:

let b = Bird2()
b.fly() // flap flap flap

let i = Insect2()
i.fly() // whirr
(i as Flier2).fly() // whirr (!!!)

let r = Rocket2()
r.fly() // zoooom
(r as Flier2).fly() // zoooom (!!!)

let r2 = AtlasRocket2()
r2.fly() // ZOOOOOM
(r2 as Rocket2).fly() // ZOOOOOM
(r2 as Flier2).fly() // ZOOOOOM (!!!)

let d = Daedalus2()
d.fly() // flap flap flap
(d as Flier2).fly() // flap flap flap

let d2 = Icarus2()
d2.fly() // fall into the sea
(d2 as Daedalus2).fly() // flap flap flap
(d2 as Flier2).fly() // flap flap flap

结果: 多态性如雨后春笋般涌现:对象的本质很重要。您可以将 Insect2 称为 Flier2,但它仍然像 Insect2 一样飞行。您可以将 Rocket2 称为 Flier2,但它仍然像 Rocket2 一样飞行。你可以将 AtlasRocket2 称为 Flier2,但它仍然像 AtlasRocket2 一样飞行。

此处的例外情况是您的问题所指出的情况——即当采用者本身没有实现该方法时。因此,我们将 Icarus2 称为 Daedalus2,你瞧,它像 Daedalus2 一样飞行,与前面的示例完全一样。不需要打开多态性,编译器从一开始就知道这一点,因为 Icarus2 的实现不是 override.