Java 为什么这是val,不是val?

Java 为什么这是val,不是val?,java,kotlin,Java,Kotlin,这似乎取决于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 == li

这似乎取决于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类注册回调时注意到了这一切,稍后将删除它,但未能删除。回调是在第三种测试样式中定义的)

原因是,每次调用接受
可运行的
的Java函数并向其中传递Kotlin函数
()->单元
时,隐式创建了一个
Runnable
,用于包装函数

当您这样做两次(
x.setRunnable(abc)
x.testContains(abc)
)时,这是两个不同的
Runnable
不相等,因此失败

这就是Kotlin的工作原理。基本上,这些调用相当于

val abc: () -> Unit = {}

val x = MyJavaClass()

x.setRunnable(Runnable(abc)) // one Runnable
assertTrue(x.testContains(Runnable(abc))) // another Runnable

另外,这也是为什么在Kotlin中重新编写类时测试不会编译的原因。理由是Kotlin已经有了功能类型,应该使用它们来代替SAM接口。因此,SAM转换是Java互操作的一种手段,而不是一种完整的语言特性。

因为它是一种语言特性。JARSo Kotlin将默默地包装它,但我必须显式地将int转换为long等@韦斯顿,是的,没错。这就是SAM转换在Kotlin中的工作原理(参见我添加的文档链接)。Kotlin如何识别函数是用Java编写的?假设你拉入一个函数采用SAM类型的库。如何决定SAM转换是否编译?@marstran,它不承认它是用Java编写的,它只发现它不是用Kotlin编写的。有两种可能的情况:Kotlin代码属于当前项目时,以及使用Kotlin库时。在第一种情况下,编译器传递的是Kotlin源代码,而不是类,因此它们显然是用Kotlin编写的。在第二种情况下,当您有一个包含类的JAR时,编译器尝试在该JAR中的类中查找Kotlin元数据(反编译一个,您将看到
@metadata
注释),如果没有,它认为这些不是Kotlin类。首先,向上投票。这意味着将lambda用于平台SAM类型存在风险?e、 g:
subscribe((事件)->Unit)
但我以后不能
取消订阅(监听器)