如何使用超类型参数覆盖方法

时间:2020-08-28 12:46:44

标签: kotlin inheritance overriding

我有一个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()方法的功能?

执行此操作的好方法是什么?

1 个答案:

答案 0 :(得分:2)

尽管在评论中提出了技术替代方案,但我想补充另一种观点。我们在这里看到的是一个类设计问题,当尝试将继承用于除真正的泛化/专业化关系之外的任何事物时都会出现。

该示例声明:

  1. 每个Fruit必须能够consume另一个Fruit
  2. AppleFruit的一种。

然后这个想法是:

  • Apple不得consume任何类型的Fruit,而只能是Apple。 ?

如果Apple确实是Fruit,它将完全遵守Fruit的声明,并能够consume的任何Fruit类。由于预定的苹果Apple违反了规则1,实际上不是Fruit,并且该语言阻止您这样声明它。

尝试解决此问题(例如,通过覆盖方法中的运行时检查)伪装了潜在的问题,并给使用此类的人带来惊喜。

解决方案:

  • 仅将继承用于真正的泛化/专业化关系。如果100%肯定一个苹果是一个水果,没有任何附加条件,那么继承就是完美的选择。否则,不是。
  • 在这种情况下:重新考虑预期的语义:
    • consume的真正含义是什么?
    • 是否有一种水果消耗任意(可能是另一种的专业特性不兼容)水果的概念?
    • 还是水果的个别专业,每个水果都有自己独立的食用概念?这样一来,在consume级别上将没有通用的Fruit方法。

通过引用基类复制派生类

回答comment中的其他问题:

我如何确保这将复制SourFruit和CoreFruit的属性?

我宁愿不将SweetFruitSourFruit表示为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")
}
相关问题