Почему этот вал, а не вал?

Кажется, что это зависит от взаимодействия между 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, чем полной языковой функцией.