Kotlin 中 dynamic/double 调度的限制是什么?
What are the limits on dynamic/double dispatch in Kotlin?
我刚刚开始探索 Kotlin,我很好奇它超越 Java 的核心动态 binding/dispatch 语义有多远。
假设我编写的代码看起来像这样:
class Animal {
fun add(x:Animal) = Animal()
}
object Horse : Animal
object Donkey : Animal
object Mule : Animal
fun Horse.add(x:Horse) = Horse()
fun Horse.add(x:Donkey) = Mule()
fun main(args : Array) {
val h:Animal = Horse
val d:Animal = Donkey
val child = h + d
}
基于以上代码——我预计会发生什么?我会因为 Horse 没有实现 add(Animal) 而在运行时失败吗?它能否在上述性质的调用中准确地区分它们,其中被比较的值的编译时类型是 Animal(至少,如所写)但它们的运行时类型更具体?如果我们使用 var 而不是 val,它会改变什么吗?
提前致谢。
编辑:修改了核心代码——我看到了第一响应者强调的问题,我没有想清楚。显然我还没有真正编译过这个,我仍然在概念层面上进行探索。
另外,我会在实际的编译器中试一试,但我担心在某些情况下它可以工作,而在其他情况下它不基于某些我不完全了解的标准理解。我无法找到关于如何在 Kotlin 中实现动态调度的参考文档(对于 Java 也不确定,就此而言;几个月前我写了一些我认为可以基于 JVM 文档工作的东西,但它没有,而且我从来没有机会探究到底是为什么)。
无论如何再次感谢!
此代码根本不可编译,因为 Animal 没有任何“+”运算符。
如果他们允许对动物使用马“+”方法,
然后你会得到运行时错误,kotlin/java/etc。试图阻止。
Kotlin 不会使用运行时类型来解析方法和内容,
因为有可能产生运行时错误。
如果另一个线程在此期间将 Animal 更改为 Mule 怎么办,确切的 line/time 另一个线程何时更改 Animal 是不确定的,因此这可能会导致运行时错误。
Val 或 var 在这种情况下不会改变任何东西。
所以这里是你的代码的实际编译版本:
fun main(vararg args: String) {
val h:Animal = Horse
val d:Animal = Donkey
val child = h + d
println(child)
}
open class Animal {
fun plus(x:Animal) = Animal()
}
object Horse : Animal()
object Donkey : Animal()
object Mule : Animal()
fun Horse.plus(x:Horse) = Horse
fun Horse.plus(x:Donkey) = Mule
结果是"Animal@1906bcf8"。
据我了解,扩展方法,即 Horse.plus(x:Horse)
和 Horse.plus(x:Donkey)
,是静态调度的。那是因为它们基本上被编译为与以下 Java 代码相同的字节码:
static Horse plus(Horse $receiver, Horse x) {
return Horse.INSTANCE;
}
顺便说一下,这与 Java 8 中的默认方法有很大的不同,后者是根据运行时类型动态调度的,并且可以被覆盖。
我刚刚开始探索 Kotlin,我很好奇它超越 Java 的核心动态 binding/dispatch 语义有多远。
假设我编写的代码看起来像这样:
class Animal { fun add(x:Animal) = Animal() } object Horse : Animal object Donkey : Animal object Mule : Animal fun Horse.add(x:Horse) = Horse() fun Horse.add(x:Donkey) = Mule() fun main(args : Array) { val h:Animal = Horse val d:Animal = Donkey val child = h + d }
基于以上代码——我预计会发生什么?我会因为 Horse 没有实现 add(Animal) 而在运行时失败吗?它能否在上述性质的调用中准确地区分它们,其中被比较的值的编译时类型是 Animal(至少,如所写)但它们的运行时类型更具体?如果我们使用 var 而不是 val,它会改变什么吗?
提前致谢。
编辑:修改了核心代码——我看到了第一响应者强调的问题,我没有想清楚。显然我还没有真正编译过这个,我仍然在概念层面上进行探索。
另外,我会在实际的编译器中试一试,但我担心在某些情况下它可以工作,而在其他情况下它不基于某些我不完全了解的标准理解。我无法找到关于如何在 Kotlin 中实现动态调度的参考文档(对于 Java 也不确定,就此而言;几个月前我写了一些我认为可以基于 JVM 文档工作的东西,但它没有,而且我从来没有机会探究到底是为什么)。
无论如何再次感谢!
此代码根本不可编译,因为 Animal 没有任何“+”运算符。
如果他们允许对动物使用马“+”方法, 然后你会得到运行时错误,kotlin/java/etc。试图阻止。
Kotlin 不会使用运行时类型来解析方法和内容, 因为有可能产生运行时错误。
如果另一个线程在此期间将 Animal 更改为 Mule 怎么办,确切的 line/time 另一个线程何时更改 Animal 是不确定的,因此这可能会导致运行时错误。
Val 或 var 在这种情况下不会改变任何东西。
所以这里是你的代码的实际编译版本:
fun main(vararg args: String) {
val h:Animal = Horse
val d:Animal = Donkey
val child = h + d
println(child)
}
open class Animal {
fun plus(x:Animal) = Animal()
}
object Horse : Animal()
object Donkey : Animal()
object Mule : Animal()
fun Horse.plus(x:Horse) = Horse
fun Horse.plus(x:Donkey) = Mule
结果是"Animal@1906bcf8"。
据我了解,扩展方法,即 Horse.plus(x:Horse)
和 Horse.plus(x:Donkey)
,是静态调度的。那是因为它们基本上被编译为与以下 Java 代码相同的字节码:
static Horse plus(Horse $receiver, Horse x) {
return Horse.INSTANCE;
}
顺便说一下,这与 Java 8 中的默认方法有很大的不同,后者是根据运行时类型动态调度的,并且可以被覆盖。