簡體   English   中英

Swift 協議擴展覆蓋

[英]Swift Protocol Extensions overriding

我正在試驗 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"

簡短的回答是協議擴展不執行類多態性。 這是有一定道理的,因為協議可以被結構體或枚舉采用,並且因為我們不希望僅僅采用協議來引入不必要的動態調度。

因此,在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有一個多態的想法。

這里有兩個非常不同的問題:協議的動態行為和協議“默認”實現的解析。

  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也是符合ColorA的子類,它具有不同的color默認實現。

    所以,我們可能會質疑 Swift 對這種情況的處理(就我個人而言,我更願意看到關於這種內在模棱兩可的情況的警告),但在我看來,問題的根源在於有兩種不同的層次結構(OOP 對象層次結構)子類和協議繼承的 POP 協議層次結構),這導致了兩個相互競爭的“默認”實現。

我知道這是一個老問題,所以你可能早就轉向其他事情了,這很好。 但是,如果您仍在為重構此代碼的正確方法而苦苦掙扎,請分享一些有關此類層次結構和此協議繼承實際代表的內容的信息,我們可能會提供更具體的建議。 這是抽象示例進一步混淆問題的情況之一。 讓我們看看真正的類型/協議是什么。 (如果你有可用的代碼, http://codereview.stackexchange.com可能是更好的地方。)

我設法得到它通過定義工作colorColor和切換執行列表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"

我在嘗試通過協議實現“可選”方法時遇到了這個問題。 可以在結構中工作,在不繼承的類中工作,也可以在從實現非協議默認方法的基類繼承的類中工作,該方法可以被覆蓋。 唯一不起作用的情況是從聲明符合性但不提供自己的“非默認”實現的基類繼承的類 - 在這種情況下,協議擴展的默認值是“烘焙”到基類, 並且不能被覆蓋或重新定義。

簡單的例子:

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

注:建議的解決方案“定義color與原來的部分Color協議”,當你有繼承涉及如不解決這個問題RedBerry繼承自BlueBerry這符合協議Color

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM