在协议扩展中默认实现的协议中定义为自定义点的函数似乎不能在子类中自定义,如果基类最初没有自定义该函数,则该子类将通过基类间接继承该协议。
下面是一个简单的协议:
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-11月:
关于本文的说明:http://nomothetis.svbtle.com/the-ghost-of-swift-bugs-future来自matt注释:
我认为这与我的问题没有严格的关系,因为这篇文章没有涉及子类间接继承协议的情况(这似乎会产生影响)。在后一种情况下,即使函数是定制点(协议需求的一部分),静态分派也不明显。根据调用时推断的类型,行为不同。
本文还介绍了另一种情况,即函数在扩展中具有默认实现,而不是协议需求的一部分,并且可能隐藏子类自定义。
编辑11月17日:
可能是In Swift, why subclass method cannot override the one, provided by protocol extension in superclass的副本
发布于 2015-11-14 17:16:19
带有方法实现的协议扩展会使我们陷入这样一种情况:有时我们是多态的(对象的内部类型才是最重要的),而有时则不是(对象的外部类型或转换方式才是最重要的)。
为了探索这一点,我使用了一个测试网格,涵盖以下参数:
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被键入为它的超类火箭时,它仍然是(为了飞行目的)一个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结果:多态性已经出现在生活中:一个物体到底是什么,重要的是什么。你可以称Insect2为Flier2,但它仍然像Insect2一样飞行。你可以称Rocket2为Flier2,但它仍然像Rocket2一样飞行。你可以称AtlasRocket2为Flier2,但它仍然像AtlasRocket2一样飞行。
这里的例外情况是您的问题所指出的情况,即当采用者本身没有实现该方法时。因此,我们将Icarus2称为Daedalus2,瞧,它像Daedalus2一样飞行,与前面的例子完全一样。不需要打开多态,编译器从一开始就知道这一点,因为Icarus2 2的实现不是override。
https://stackoverflow.com/questions/33702738
复制相似问题