为什么这个val,不是val?

这似乎是依赖于Java和Kotlin之间的交互,首先是Java类:

public class MyJavaClass { private Runnable q; public void setRunnable(final Runnable listener) { q = listener; } public boolean testContains(final Runnable listener) { return q == listener; } } 

现在Kotlin测试:

 class JavaInteractionTests { @Test fun `anonymous`() { val abc = object : Runnable { override fun run() { } } val x = MyJavaClass() x.setRunnable(abc) assertTrue(x.testContains(abc)) } @Test fun `lambda`() { val abc = Runnable { } val x = MyJavaClass() x.setRunnable(abc) assertTrue(x.testContains(abc)) } @Test fun `function`() { val abc: () -> Unit = {} val x = MyJavaClass() x.setRunnable(abc) assertTrue(x.testContains(abc)) } } 

最后一次测试失败,所以它会出现我的val实际上不是一个val

错误或可解释,预期的行为?

请注意,如果Java类在Kotlin中是这样定义的,则最后的测试不会编译:

 class MyKtClass { private var q: Runnable? = null fun setRunnable(listener: Runnable) { q = listener } fun testContains(listener: Runnable): Boolean { return q === listener } } 

(我在注册一个Java类的回调时注意到了所有这些,稍后将其删除,并且未能被删除。回调在第三种测试样式中定义)

原因在于,每次调用接受Runnable的Java函数并将Kotlin函数() -> Unit传递给它时,隐式创建一个包装该函数的Runnable

而当你这样做了两次( x.setRunnable(abc)x.testContains(abc) ),这两个不同的Runnable s是不相等的,因此失败了。

这就是Kotlin中SAM转换的工作原理。 基本上这些电话相当于

 val abc: () -> Unit = {} val x = MyJavaClass() x.setRunnable(Runnable(abc)) // one Runnable assertTrue(x.testContains(Runnable(abc))) // another Runnable 

此外, Kotlin不支持在Kotlin中定义的函数的SAM转换 ,这就是为什么当您在Kotlin中重新编写类时,您的测试不能编译。 原因是Kotlin已经具有功能类型,应该使用它们来代替SAM接口。 所以,SAM转换不是一个完整的语言功能,而是Java互操作的一个手段。