Кажется, что это зависит от взаимодействия между 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; } }
Теперь тесты Котлина:
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, которая принимает Runnable
и передает функцию Kotlin () -> Unit
в нее, Runnable
неявно создается, которая обертывает эту функцию.
И когда вы делаете это дважды ( x.setRunnable(abc)
и x.testContains(abc)
), это два разных Runnable
s, которые не равны друг другу, следовательно, сбой.
Вот как работает преобразование SAM в Котлин. В принципе, эти вызовы эквивалентны
val abc: () -> Unit = {} val x = MyJavaClass() x.setRunnable(Runnable(abc)) // one Runnable assertTrue(x.testContains(Runnable(abc))) // another Runnable
Кроме того, Kotlin не поддерживает преобразования SAM для функций, определенных в Kotlin , поэтому ваш тест не компилируется при повторной записи класса в Kotlin. Обоснование заключается в том, что у Kotlin уже есть функциональные типы, и они должны использоваться вместо SAM-интерфейсов. Таким образом, преобразование SAM является скорее средством взаимодействия Java, чем полной языковой функцией.