这些协议让我做恶梦。
我正在尝试实现一些符合它们的协议和类,这样我就可以拥有默认实现,但通过扩展协议/类可以获得自定义实现。到目前为止,这就是我所拥有的:
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'
}
}
我做错了什么?
答案 0 :(得分:4)
从类型继承时,无法缩小已覆盖函数中使用的参数类型。这是您通过将参数从类型ProtA
(更通用的类型)更改为ProtB
(更具体的类型)来完成的。
这是Liskov substitution principle的结果。简而言之,子类必须能够(至少)超类可以做的所有事情。
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
。这样做可以保持您的班级使用“车辆”的能力。Car
的函数覆盖返回类型为Vehicle
的函数。这样做意味着返回的值比超级保证应该的“强大”。Car
的函数覆盖返回类型为RaceCar
的函数。这样做意味着返回的值“更强大”,并且它至少与超类守护者一样多。答案 1 :(得分:2)
没有错。您应该确保声明在语义上是一致的。 您应该创建ProtD,使用ProtB参数声明该方法,或者解包已获取的ParamA参数以将其用作ProtB。
func doSomething(parameter: Int, with anotherParameter: ProtB) {
if let a = anotherParameter as? ProtA {
print (a.aThirdProperty)
}
}