我有一个Fruit
类,
open class Fruit(var taste: String) {
open fun consume(from: Fruit) {
taste = from.taste
}
}
我有一个Apple
类,它像这样扩展了Fruit
类。
class Apple(
var color: String,
taste: String
): Fruit(taste) {
// caution: method overrides nothing
override fun consume(from: Apple) {
super.consume(from)
color = from.color
}
}
这是我的使用代码:
val fruit: Fruit = Apple(color = "green", taste = "sweet")
val badFruit = Apple(
color = anyOf("red", "blue", "golden"),
taste = anyOf("sour+", "sweet+", "chilli+")
)
fruit.consume(from = badFruit)
println("BadFruit: $badFruit")
println("InfectedFruit: $fruit")
我无法覆盖Apple
类中的以下方法:
override fun consume(from: Apple) {
super.consume(from)
color = from.color
}
要正确覆盖此方法,我需要传入Fruit类的实例(与super方法一样)。如果这样做,我将始终必须检查Fruit
实例是否实际上是Apple
实例。但是,它不应该只因为Apple
扩展了Fruit
而与前者一起工作吗?
如何实现在consume()
上调用fruit: Fruit = Apple(...)
时实际上调用Apple#consume()
方法的功能?
执行此操作的好方法是什么?
答案 0 :(得分:2)
尽管在评论中提出了技术替代方案,但我想补充另一种观点。我们在这里看到的是一个类设计问题,当尝试将继承用于除真正的泛化/专业化关系之外的任何事物时都会出现。
该示例声明:
Fruit
必须能够consume
另一个Fruit
。Apple
是Fruit
的一种。然后这个想法是:
Apple
不得consume
任何类型的Fruit
,而只能是Apple
。 ?如果Apple
确实是Fruit
,它将完全遵守Fruit
的声明,并能够consume
的任何Fruit
类。由于预定的苹果Apple
违反了规则1,实际上不是Fruit
,并且该语言阻止您这样声明它。
尝试解决此问题(例如,通过覆盖方法中的运行时检查)伪装了潜在的问题,并给使用此类的人带来惊喜。
解决方案:
consume
的真正含义是什么?consume
级别上将没有通用的Fruit
方法。回答comment中的其他问题:
我如何确保这将复制SourFruit和CoreFruit的属性?
我宁愿不将SweetFruit
和SourFruit
表示为CoreFruit
的专业化。诸如酸甜之类的风味是水果的特征,并且可以更好地表现为特性。
但是我可以稍微扩展一下您的示例,然后建议一个包含clone()
函数的类设计,该函数在基类Flavor
上提供深层复制功能。请注意,输出显示了克隆对象的不同哈希码:
data class Fruit(var weight: Double, var flavors: MutableList<Flavor>) {
fun clone(): Fruit {
return Fruit(weight, flavors.map { it.clone() }.toMutableList())
}
}
abstract class Flavor {
abstract fun clone(): Flavor
}
class SweetFlavor(var intensity: Int, var isHealthy: Boolean) : Flavor() {
override fun clone(): Flavor {
return SweetFlavor(intensity, isHealthy)
}
}
class SourFlavor(var intensity: Int) : Flavor() {
override fun clone(): Flavor {
return SourFlavor(intensity)
}
}
fun main() {
val apple = Fruit(0.2, mutableListOf(SweetFlavor(4, true), SourFlavor(2)))
val lemon = Fruit(0.35, mutableListOf(SourFlavor(9)))
val appleClone = apple.clone()
println("apple: $apple")
println("lemon: $lemon")
println("appleClone: $appleClone")
appleClone.weight += 0.5
appleClone.flavors[0] = SweetFlavor(6, false)
println("apple: $apple")
println("appleClone: $appleClone")
}