似乎在协议扩展中具有默认实现的协议中定义为自定义点的函数无法在通过基类间接继承协议的子类中进行自定义(如果该基类在第一个时没有自定义函数)的地方。
这是一个简单的协议:
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"
这是预期的行为吗?
编辑14-nov:
关于这篇文章的注意事项:来自http://nomothetis.svbtle.com/the-ghost-of-swift-bugs-future评论的matt:
我认为这与我的问题并不严格相关,因为该文章没有涵盖子类间接继承协议的情况(这似乎有所不同)。在后一种情况下,即使函数是自定义点(协议要求的一部分),对我来说也不会发生静态调度。根据呼叫时推断的类型,行为会有所不同。
本文介绍了另一种情况,其中函数在扩展中具有默认实现,而不是协议要求的一部分,并且可能影响子类自定义。
编辑17 nov:
可能是In Swift, why subclass method cannot override the one, provided by protocol extension in superclass
的副本答案 0 :(得分:2)
带有方法实现的协议扩展使我们处于这样的情况:有时我们是多态的(对象的内部类型是重要的),有时我们不是(对象是外部输入或强制转换的方式)。< / p>
为了探索这一点,我使用了一个涵盖以下参数的测试网格:
协议本身是否也需要这种方法?
采用者是结构还是类?
采用者是否实施了该方法?
我们首先回答第一个问题是否定的。所以,这是类型声明:
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”,协议本身包含该方法作为要求:
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
。