Swift协议扩展的覆盖重写

67

我正在尝试使用Swift协议扩展,但我发现这种行为相当令人困惑。你能帮我知道如何获得我想要的结果吗?

请查看代码的最后4行的注释。(如果需要,您可以将其复制粘贴到Xcode7 playground中)。谢谢!

protocol Color { }
extension Color {  var color : String { return "Default color" } }

protocol RedColor: Color { }
extension RedColor { var color : String { return "Red color" } }


protocol PrintColor {
    
     func getColor() -> String
}

extension PrintColor where Self: Color {
    
    func getColor() -> String {
        
        return color
    }
}


class A: Color, PrintColor { }
class B: A, RedColor { }


let colorA = A().color // is "Default color" - OK
let colorB = B().color // is "Red color" - OK


let a = A().getColor() // is "Default color" - OK
let b = B().getColor() // is "Default color" BUT I want it to be "Red color"
5个回答

68
答案:
简短的回答是协议扩展不支持类多态性。这很有道理,因为协议可以被结构体或枚举采用,并且我们不希望仅仅采用协议就引入不必要的动态调度。
因此,在`getColor()`中,`color`实例变量(可能更准确地写为`self.color`)的含义与您想象中的不同,因为您在以类的多态方式思考,而协议并没有这种特性。因此,这段代码可以正常工作:
let colorB = B().color // is "Red color" - OK

因为您正在要求一个来解析color,但这并不会达到您的预期:

let b = B().getColor() // is "Default color" BUT I want it to be "Red color"

这是因为getColor方法完全在协议扩展中定义。您可以通过在B中重新定义getColor来解决问题:

class B: A, RedColor {
    func getColor() -> String {
        return self.color
    }
}

现在调用了类的getColor方法,它对self对象具有多态性的想法。


4
这里有一个很好的讨论:http://nomothetis.svbtle.com/the-ghost-of-swift-bugs-future - matt
谢谢您的回答。让我重新定义我的问题:是否有一种方法可以通过将协议添加到类来简单地修改某些实例变量?假设我们有类AA.text"Hello"。但是,类A:Protocol会导致A.text变为"Ciao"吗? - VojtaStavik
不,那样毫无意义。再次想想协议的作用是什么。如果仅仅采用协议就比类本身更有权力来确定某些东西应该是什么或做什么,那将是非常糟糕的。然而,请阅读我给你指出的文章,因为它表明,如果一个对象引用被定义为协议,协议的实现可以优先于采用者的实现。 - matt
1
@VojtaStavik 限制扩展到多个协议 extension Lang where Self is Lang & Italian{print("Ciao")} - Sentry.co

14

这里涉及到两个非常不同的问题:协议的动态行为和协议“默认”实现的解析。

  1. 在动态方面,我们可以通过一个简单的例子来说明问题:

    protocol Color { }
    
    extension Color {
        var color: String { return "Default color" }
    }
    
    class BlueBerry: Color {
        var color: String { return "Blue color" }
    }
    
    let berry = BlueBerry()
    print("\(berry.color)")                 // prints "Blue color", as expected
    
    let colorfulThing: Color = BlueBerry()
    print("\(colorfulThing.color)")         // prints "Default color"!
    

    正如您在答案中指出的那样,如果将color定义为原始Color协议的一部分(即教导编译器合理地期望遵守该协议的类实现此方法,并且仅在找不到实现时使用该协议的实现),则可以获得动态行为:

    protocol Color {
        var color: String { get }
    }
    
    ...
    
    let colorfulThing: Color = BlueBerry()
    print("\(colorfulThing.color)")         // now prints "Blue color", as expected
    
  2. 现在,在你的答案中,你质疑当BA的子类时为什么会有点问题。

    我认为记住协议扩展中的方法实现是“默认”实现,即如果符合协议的类没有自己的实现,则使用这些实现。你的情况中混淆的源头来自于B符合RedColor,它具有对color的默认实现,但B也是A的子类,后者符合Color,它对color有不同的默认实现。

    因此,我们可能会对Swift处理这种情况提出异议(个人而言,我宁愿看到警告这种固有的模糊局面),但在我看来,问题的根源在于存在两个不同的层次结构(子类的OOP对象层次结构和协议继承的POP协议层次结构),从而导致两个竞争的“默认”实现。

我知道这是一个旧问题,所以你可能已经转向其他事情了,这没关系。但如果你仍然在困扰如何正确地重构这段代码,请分享一下这个类层次结构和协议继承实际上代表什么,我们可能能够提供更具体的建议。这是其中一个抽象例子只会进一步混淆问题的情况。让我们看看类型/协议真正是什么。(如果你有工作代码,http://codereview.stackexchange.com可能是更好的地方。)


5

我通过在Color上定义color并切换B的实现列表来使其工作。但是,如果B必须是A,那么这并没有太大的帮助。

protocol Color {
    var color : String { get }
}

protocol RedColor: Color {

}

extension Color {
    var color : String {
        get {return "Default color"}
    }
}

extension RedColor {
    var color : String {
        get {return "Red color"}
    }
}

protocol PrintColor {
    func getColor() -> String
}

extension PrintColor where Self: Color {
    func getColor() -> String {
        return color
    }
}

class A : Color, PrintColor {

}

class B : RedColor, PrintColor {

}

let a = A().getColor() // "Default color"
let b = B().getColor() // "Red color"

4
注意:提出的解决方案“将color定义为原始Color协议的一部分”不能解决涉及继承的问题,例如RedBerry继承自符合协议ColorBlueBerry
protocol Color {
    var color: String { get }
}

extension Color {
    var color: String { return "Default color" }
}

class BlueBerry: Color {
    //    var color: String { return "Blue color" }
}

class RedBerry: BlueBerry {
    var color: String { return "Red color" }
}

let berry = RedBerry()
print(berry.color)             // Red color

let colorfulThing: Color = RedBerry()
print(colorfulThing.color)     // Actual: Default color, Expected: Red color

4
我在尝试通过协议实现一个“可选”方法时遇到了这个问题。在结构体和没有继承的类以及从实现了非协议默认方法且可被覆盖的基类继承的类中,它可以被实现。唯一无法使用的情况是继承于已声明符合但未提供自己的“非默认”实现的基类 - 在这种情况下,协议扩展的默认值被“嵌入”到基类中,不能被覆盖或重新定义。
简单示例:
typealias MyFunction = () -> ()
protocol OptionalMethod {
    func optionalMethod() -> MyFunction?
    func executeOptionalMethod()
}
extension OptionalMethod {
    func optionalMethod() -> MyFunction? { return nil }
    func executeOptionalMethod() {
        if let myFunc = self.optionalMethod() {
            myFunc()
        } else {
            print("Type \(self) has not implemented `optionalMethod`")
        }
    }
}

class A: OptionalMethod {
}
class B: A {
    func optionalMethod() -> MyFunction? {
        return { print("Hello optional method") }
    }
}
struct C: OptionalMethod {
    func optionalMethod() -> MyFunction? {
        return { print("Hello optionalMethod") }
    }
}
class D: OptionalMethod {
    func optionalMethod() -> MyFunction? {
        return { print("Hello optionalMethod") }
    }
}
class E: D {
    override func optionalMethod() -> MyFunction? {
        return { print("Hello DIFFERENT optionalMethod") }
    }
}
/* Attempt to get B to declare its own conformance gives:
// error: redundant conformance of 'B2' to protocol 'OptionalMethod'
class B2: A, OptionalMethod {
    func optionalMethod() -> MyFunction? {
        return { print("Hello optional method") }
    }
}
*/
class A2: OptionalMethod {
    func optionalMethod() -> MyFunction? {
        return nil
    }
}
class B2: A2 {
    override func optionalMethod() -> MyFunction? {
        return { print("Hello optionalMethod") }
    }
}

let a = A() // Class A doesn't implement & therefore defaults to protocol extension implementation
a.executeOptionalMethod() // Type __lldb_expr_201.A has not implemented `optionalMethod`
let b = B() // Class B implements its own, but "inherits" implementation from superclass A
b.executeOptionalMethod() // Type __lldb_expr_205.B has not implemented `optionalMethod`
let c = C() // Struct C implements its own, and works
c.executeOptionalMethod() // Hello optionalMethod
let d = D() // Class D implements its own, inherits from nothing, and works
d.executeOptionalMethod() // Hello optionalMethod
let e = E() // Class E inherits from D, but overrides, and works
e.executeOptionalMethod() // Hello DIFFERENT optionalMethod
let a2 = A2() // Class A2 implements the method, but returns nil, (equivalent to A)
a2.executeOptionalMethod() // Type __lldb_expr_334.A2 has not implemented `optionalMethod`
let b2 = B2() // Class B2 overrides A2's "nil" implementation, and so works
b2.executeOptionalMethod() // Hello optionalMethod

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接