Как kotlin делает setOnClickListener принимать функции как параметр

В kotlin мы можем использовать setOnClickListener() следующим образом:

view.setOnClickListener { println("Hello") }

Но если я определяю свой собственный интерфейс, я могу передать анонимный объект следующим образом:

obj.setMyListener(object: MyListener() {
    ...
})

Мне просто интересно, как они принимают setOnClickListener() функцию, а не анонимный объект.

Ответ 1

Согласно документам Котлина о Java interop, для функционального интерфейса, определенного на Java, вы можете использовать преобразование SAM.

Как и Java 8, Kotlin поддерживает конверсии SAM. Это значит, что Функциональные литералы Котлин могут быть автоматически преобразованы в реализация Java-интерфейсов с помощью одного метода, отличного от значения по умолчанию, до тех пор, пока типы параметров метода интерфейса совпадают типы параметров функции Котлина.

val runnable = Runnable { println("This runs in a runnable") }

val executor = ThreadPoolExecutor()
// Java signature: void execute(Runnable command)
executor.execute { println("This runs in a thread pool") }

Однако у Kotlin есть функциональные типы, поэтому преобразование SAM не работает для интерфейсов, определенных в Kotlin:

Также обратите внимание, что эта функция работает только для Java interop; так как Котлин имеет надлежащие типы функций, автоматическое преобразование функций в реализация интерфейсов Kotlin не нужна, и поэтому не поддерживается.


Возможные решения:

  • Определите интерфейс в Java. Полезно, если это библиотека/код, который может использоваться из кода Java.
  • Сделать метод получателем функции в качестве аргумента:

    // Example listener receives a bool and return unit.  
    fun setMyListener(listener: (isChecked: Bool) -> Unit) { ... }  
    // Usage:  
    obj.setMyListener { isChecked -> }
    
  • Использовать псевдоним типа (поддерживается только в Kotlin 1.1 +):

    typealias MyListener = (Bool) -> Unit
    fun setMyListener(listener: MyListener) { ... }