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

时间:2017-07-28 11:37:15

标签: 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 == 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

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

注意,如果在Kotlin中定义了Java类,那么最后一个测试就不会编译:

class MyKtClass {
    private var q: Runnable? = null

    fun setRunnable(listener: Runnable) {
        q = listener
    }

    fun testContains(listener: Runnable): Boolean {
        return q === listener
    }
}

(在注册Java类回调时注意到这一切,以后删除它并且无法删除。回调是在第3次测试样式中定义的)

1 个答案:

答案 0 :(得分:7)

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

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

这就是SAM conversion在Kotlin中的作用。基本上,这些调用等同于

val abc: () -> Unit = {}

val x = MyJavaClass()

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

另外,Kotlin does not support SAM conversions for functions that are defined in Kotlin,这就是为什么在Kotlin中重写类时你的测试无法编译的原因。原因是Kotlin已经具有功能类型,应该使用它们代替SAM接口。因此,SAM转换相当于Java互操作而不是完整的语言功能。