[英]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.