子类中的方法自定义未调用 [英] Method customization in sub class not called

查看:69
本文介绍了子类中的方法自定义未调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

似乎,在协议扩展中具有默认实现的协议中定义为协议中的自定义点的函数,如果该基类最初没有自定义该函数,则无法在通过基类间接继承该协议的子类中自定义该函数.地方.

It seems that a function defined as a customization point in a protocol with a default implementation in protocol extension cannot be customized in a subclass that inherits the protocol indirectly via a base class if that base class did not customize the function at the first place.

这是一个简单的协议:

protocol MyProtocol
{
    func myFunc() -> String
}

使用默认实现:

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

让我们创建一个基类和一个子类,如下所示:

Let's create a base class and a sub-class like this:

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"

现在在基类中进行了自定义:

Now with a customization in the base 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来自 matt 的.com/the-ghost-of-swift-bugs-future 注释:

Note about about this article: http://nomothetis.svbtle.com/the-ghost-of-swift-bugs-future from matt comment:

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

I think it is not strictly related to my question as the article does not cover the case where the subclass inherits the protocol indirectly (which seems to make a difference). In that later case, it is not obvious to me that a static dispatch does happen even if the function is a customization point (part of the protocol requirements). Depending on the inferred type at call time, behavior differs.

本文介绍了另一种情况,即该功能在扩展中具有默认实现,而不是协议要求的一部分,并且可能会掩盖子类的自定义.

The article covers the other case where the function has the default implementation in the extension while not part of the protocol requirements and may shadow subclass customizations.

编辑11月17日:

可能是推荐答案

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

A protocol extension with a method implementation puts us into a situation where sometimes we are polymorphic (the object's internal type is what matters) and sometimes we are not (the way the object is externally typed or cast is what matters).

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

To explore this, I use a test grid covering the following parameters:

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

  • Does the protocol itself also require the method?

采用者是结构还是类?

采用者是否实现该方法?

Does the adopter implement the method?

我们从第一个问题的答案是否"开始.因此,这是类型声明:

We start with the answer to the first question being NO. So, here are the type declarations:

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实现.所有必要的信息都会在编译时出现.通常,不需要动态调度.

Result: How an object is typed is important. In effect, the compiler knows merely from the way the object is typed where to look for the fly implementation that will be called; all the necessary information is present at compile time. There is, in general, no need for dynamic dispatch.

例外是AtlasRocket,这是一个子类,其超类具有自己的实现.当将AtlasRocket键入其超类Rocket时,它仍然是AtlasRocket(出于飞行目的).但这并不奇怪,因为这是子类/超类的情况,其中多态性和动态调度有效.显然,我们不会仅仅因为故事中还有 一个协议扩展而关闭动态分发.

The exception is the AtlasRocket, a subclass whose superclass has its own implementation. When the AtlasRocket is typed as its superclass, Rocket, it is still (for purposes of flying) an AtlasRocket. But this is not surprising, because this is a subclass/superclass situation, where polymorphism and dynamic dispatch are in effect; obviously we're not going to turn off dynamic dispatch merely because there is also a protocol extension in the story.

现在,第一个问题的答案是是".类型声明与以前完全相同,除了我在所有类型的名称上添加了"2",并且协议本身包含了该方法的要求:

Now the answer to the first question is YES. The type declarations are exactly the same as before, except that I've added a "2" to the names of all the types, and the protocol itself contains the method as a requirement:

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":

And here are the tests; they are the same tests, with a "2" added to the names of all the types:

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一样飞行.

Result: Polymorphism has sprung to life: what an object really is, matters. You can call an Insect2 a Flier2, but it still flies like an Insect2. You can call a Rocket2 a Flier2, but it still flies like a Rocket2. You can call an AtlasRocket2 a Flier2, but it still flies like an AtlasRocket2.

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

The exceptional case here is the one pointed out by your question — namely, when the adopter itself has no implementation of the method. Thus, we call an Icarus2 a Daedalus2, and lo and behold, it flies like a Daedalus2, exactly as in the previous example. There was no need to switch on polymorphism, and the compiler knew this from the outset, because Icarus2's implementation is not an override.

这篇关于子类中的方法自定义未调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆