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)
函数。
这就是为什么我们说扩展是静态解析的。编译器决定在编译时调用哪个函数。考虑到对象的实际类型,虚函数在运行时解析。
我正在学习 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)
函数。
这就是为什么我们说扩展是静态解析的。编译器决定在编译时调用哪个函数。考虑到对象的实际类型,虚函数在运行时解析。