Swift2 未调用子类中的方法自定义

Swift2 未调用子类中的方法自定义,swift2,protocols,Swift2,Protocols,在协议扩展中具有默认实现的协议中定义为自定义点的函数似乎无法在通过基类间接继承协议的子类中进行自定义,如果该基类最初没有自定义该函数 以下是一个简单的协议: protocol MyProtocol { func myFunc() -> String } 使用默认实现: extension MyProtocol { func myFunc() -> String { return "hello from extension" } }

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

以下是一个简单的协议:

protocol MyProtocol
{
    func myFunc() -> String
}
使用默认实现:

extension MyProtocol
{
    func myFunc() -> String
    {
        return "hello from extension"
    }
}
让我们创建一个基类和一个子类,如下所示:

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

关于本文的注释:来自评论:

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

本文介绍了另一种情况,即函数在扩展中具有默认实现,而不是协议需求的一部分,并且可能会隐藏子类定制

编辑11月17日:


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

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

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

  • 采用者是结构还是类

  • 采用者是否实现了该方法


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是个例外,它是一个子类,其超类有自己的实现。当AtlasRocket被键入其超类Rocket时,它仍然是(为了飞行目的)AtlasRocket。但这并不奇怪,因为这是一个子类/超类的情况,多态性和动态调度是有效的;显然,我们不会仅仅因为故事中还有一个协议扩展就关闭动态调度


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
结果:多态性出现了:一个对象到底是什么很重要。你可以称昆虫2为飞行物2,但它仍然像昆虫2一样飞行。你可以把火箭称为飞行物,但它仍然像火箭一样飞行。你可以把AtlasRocket2称为飞行者2,但它仍然像AtlasRocket2一样飞行


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

,感谢链接。问题编辑:“我认为这篇文章确实涵盖了你所指出的差异。”。这篇文章使用的是struct,它不能涵盖我的观点,因为我正在对一个基类进行子类化。在您的句子“我们不会仅仅因为故事中还有协议扩展就关闭动态调度”和“没有必要打开多态性”中,您似乎选择了多态性还是非多态性,动态调度与否。我不太明白这一点。如果我使用默认的扩展实现对一个本身符合协议的基类进行子类化,那么即使该子类间接符合协议,我也无法在我的子类中对其进行自定义。这是一个bug还是一个正常的行为?我不知道你说的这些是什么意思。我们所能做的就是描述Swift语言的现状。它并不总是这样,也许也不会总是这样,但现在它的行为就是这样。你唯一的原始问题是:这是预期的吗?我的回答是:是的,因为这是真实的和众所周知的。我看不出你还在找什么。如果没有答案能让你满意,请删除你所谓的问题,因为这根本不是问题。很好,谢谢你。如果你有任何关于这种众所周知的行为的链接,我将非常感兴趣。