Kotlin 多态性混淆

Kotlin Polymorphism Confusion

我正在学习 kotlin 的教程,运行 进入这个例子。

open class AquariumPlant(val color: String, private val size: Int)

class GreenLeafyPlant(size: Int) : AquariumPlant("green", size)

fun AquariumPlant.print() = println("AquariumPlant")
fun GreenLeafyPlant.print() = println("GreenLeafyPlant")

val plant = GreenLeafyPlant(size = 10)
plant.print()
println("\n")
val aquariumPlant: AquariumPlant = plant
aquariumPlant.print()  // what will it print?

好吧,这显然打印了“Aquarium Plant”而不是“GreenLeafyPlant”。我对此感到有点困惑,所以我用这一小段代码对其进行了测试。

open class Aquarium {
    open fun printSize() {
        println("hello")
    }
}

class TowerTank: Aquarium() {
    override fun printSize() {
        println("rawr")
    }
}

fun main() {
    towerTank = TowerTank()
    (towerTank as Aquarium).printSize()
}

所以这会打印“rawr”而不是“hello”。我的问题是为什么它不打印“hello”?这两个例子不是自相矛盾吗?功能扩展如何造成这种行为差异?抱歉,如果这看起来像是一个愚蠢的问题,我是 Kotlin 的新手,您可能会说。

要理解这一点,我们需要了解扩展的工作原理。扩展不会神奇地将新成员添加到现有 类。这在 Java 和 Kotlin 中在技术上都是不可能的。相反,它们在 Java 中用作旧的静态实用函数。以成员身份访问它们只是一种语法糖。

第一个例子与这些函数非常相似:

fun print(plant: AquariumPlant) = println("AquariumPlant")
fun print(plant: GreenLeafyPlant) = println("GreenLeafyPlant")

为了更清楚,我们可以重命名这些函数:

fun printAquariumPlant(plant: AquariumPlant) = println("AquariumPlant")
fun printGreenLeafyPlant(plant: GreenLeafyPlant) = println("GreenLeafyPlant")

现在,很明显如果我们有这样的对象:

val aquariumPlant: AquariumPlant = GreenLeafyPlant(size = 10)

然后我们只能用它调用printAquariumPlant()函数,它会打印AquariumPlant,而不是GreenLeafyPlant。尽管 aquariumPlant 实际上是一个 GreenLeafyPlant 对象。

如果我们后退一步,再次将它们重命名为 print,那么什么都不会真正改变。 aquariumPlant 变量是 AquariumPlant 类型(即使它包含 GreenLeafyPlant 对象),所以编译器选择 print(AquariumPlant) 函数。

这就是为什么我们说扩展是静态解析的。编译器决定在编译时调用哪个函数。考虑到对象的实际类型,虚函数在运行时解析。