这个似乎依赖于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次测试样式中定义的)
答案 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互操作而不是完整的语言功能。