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 中的默认方法有很大的不同,后者是根据运行时类型动态调度的,并且可以被覆盖。