簡體   English   中英

Swift,基於擴展協議的類不符合原始協議

[英]Swift, classes based on extended protocol don't conform to original protocol

這些協議讓我做噩夢。

我正在嘗試實現幾個符合它們的協議和類,這樣我就可以有默認實現,但是通過擴展協議/類可以使用自定義實現。 到目前為止,這就是我所擁有的:

protocol ProtA {
    var aProperty: String { get set }
    var anotherProperty:String { get set }

    func aFunc (anArgument: String) -> String
}

protocol ProtB: ProtA {
    var aThirdProperty: String { get set }
}

protocol ProtC {
    func doSomething(parameter: Int, with anotherParameter: ProtA)
}

class ClassA: ProtA {
    var aProperty: String = "Hello, I'm a String."
    var anotherProperty: String = "I'm not the same String."

    func aFunc (anArgument: String) -> String {
        return anArgument
    }
}

class ClassB: ProtB {

    var aProperty: String = "Hello, I'm a String."
    var anotherProperty: String = "I'm not the same String."
    var aThirdProperty: String = "I'm yet another String!"

    func aFunc (anArgument: String) -> String {
        return anArgument
    }
}



class ClassC: ProtC {

func doSomething(parameter: Int, with anotherParameter: ProtA) {
    print (anotherParameter.aProperty) // Works fine.

}

}

那么,如果我這樣做

class ClassC: ProtC {

    func doSomething(parameter: Int, with anotherParameter: ProtA) {
        print (anotherParameter.aProperty) // Works fine.

    }

}

但是,如果我這樣做

class ClassD: ProtC {

    func doSomething(parameter: Int, with anotherParameter: ProtA) {

        print (anotherParameter.aThirdProperty) // Value of type 'ProtA' has no member 'aThirdProperty'

    }

}

而且,如果我這樣做

class ClassE: ProtC {

    func doSomething(parameter: Int, with anotherParameter: ProtB) {

        print (anotherParameter.aThirdProperty) // Type 'ClassE' does not conform to protocol 'ProtC'

    }

}

我究竟做錯了什么?

問題

從類型繼承時,您不能縮小覆蓋函數中使用的參數的類型。 這就是您通過將參數從類型ProtA (更通用的類型)更改為ProtB (更具體的類型) ProtB

這是Liskov 替換原則的結果 簡單地說,子類必須能夠(至少)做超類可以做的所有事情。

ProtC確定所有符合的類型都有一個函數func doSomething(parameter: Int, with anotherParameter: ProtA) ,類型為(Int, ProtA) -> Void)

您在ClassE修改的函數類型為(Int, ProtB) -> Void 但是,此函數不能再替代它所覆蓋的函數。

假設有可能做你嘗試過的事情。 看看會發生什么:

let instanceConformingToProtA: ProtA = ClassA()

let instanceConformingToProtC: ProtC = ClassE()

// This would have to be possible:
instanceConformingToProtC(parameter: 0, amotherParameter: instanceConformingToProtA)

但是, ClassE()不能instanceConformingToProtA作為其第二個參數的有效參數,因為它是ProtA而不是必需的ProtB

解決方案

此問題的解決方案完全取決於您要實現的目標。 在繼續之前,我需要更多信息。

根據經驗,當覆蓋繼承的成員時:

  • 參數類型必須相同,或更通用。
    • 例如,您不能用Car類型的參數覆蓋函數,並將參數類型更改為RaceCar 這樣做會破壞您的類使用RaceCar的能力,而 LSP 必須能夠做到這一點。
    • 例如,您可以使用Car類型的參數覆蓋函數,並將參數更改為Vehicle 這樣做可以保留您的類使用“車輛”的能力。
  • 返回類型必須相同或更具體。
    • 例如,您不能用返回Vehicle的函數覆蓋返回類型為Car的函數。 這樣做意味着返回的值比超類保證的“功能更弱”。
    • 例如,您可以使用返回RaceCar的函數覆蓋返回類型為Car的函數。 這樣做意味着返回的值“更強大”,並且它的作用至少與超類所保證的一樣多。

沒有什么不對的。 您應該確保聲明在語義上是一致的。 您應該創建 ProtD,使用 ProtB 參數聲明方法,或者解開獲取的 ParamA 參數以將其用作 ProtB。

func doSomething(parameter: Int, with anotherParameter: ProtB) {
     if let a = anotherParameter as? ProtA {
          print (a.aThirdProperty)
     }

}

暫無
暫無

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

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