Использование функциональных интерфейсов с функциями функций в Котлине

При вызове кода Java из Kotlin происходит преобразование SAM, так что Java-код выглядит следующим образом:

adapter.setOnClickListener(new OnClickListener() { @Override public void onClick(View view, int position) { // Do stuff here } }); 

Может выглядеть так:

 adapter.setOnClickListener { view, position -> // Do stuff } 

Теперь я работаю над проектом Kotlin, и я хочу определить функциональный интерфейс как прослушиватель событий:

 interface OnSomeActionListener { fun onSomeAction(parameter1: Int, parameter2: String) } 

В SomeClass меня есть функция для установки слушателя:

  ... private var onSomeActionListener: OnSomeActionListener? = null fun setOnSomeActionListener(listener: OnSomeActionListener) { onSomeActionListener = listener } ... 

И когда я создаю экземпляр этого класса и пытаюсь вызвать функцию setter, я делаю это так:

 val thing = SomeClass() thing.setOnSomeActionListener(object : OnSomeActionListener { override fun onSomeAction(parameter1: Int, parameter2: String) { // Do stuff here } }) 

Я знаю, что Kotlin имеет функциональные типы, поэтому не поддерживает преобразование SAM с различных сайтов, таких как этот .

Я немного читал о типах функций, но раньше я их не использовал.

Как мне переписать мой код, чтобы я мог вызвать функцию setter, как это?

 val thing = SomeClass() thing.setOnSomeActionListener { parameter1, parameter2 -> // Do stuff here } 

,

Тип функции выглядит следующим образом:

 (Parameters) -> ReturnType 

В вашем случае вместо использования типа интерфейса вы можете использовать (View, Int) -> Unit . Это будет выглядеть примерно так:

 private var onSomeActionListener: ((View, Int) -> Unit)? = null fun setOnSomeActionListener(listener: (View, Int) -> Unit) { onSomeActionListener = listener } private fun callSomeActionListener(view: View, position: Int) { onSomeActionListener?.invoke(view, position) } 

Добавить имена

В функциональных типах вы также можете указать имена параметров. Это не сильно меняется под капотом, но они могут добавить некоторую ясность здесь и в вызывающем коде, что приятно.

 (view: View, position: Int) -> Unit 

Использование псевдонима типа

Чтобы избежать необходимости вводить (View, Int) -> Unit каждый раз, вы можете определить тип:

 typealias OnSomeActionListener = (view: View, position: Int) -> Unit 

Чтобы ваш код теперь выглядел так:

 private var onSomeActionListener: OnSomeActionListener? = null fun setOnSomeActionListener(listener: OnSomeActionListener?) { onSomeActionListener = listener } 

И назвать это:

 val thing = SomeClass() thing.setOnSomeActionListener { view, position -> // Do stuff here } 

Ну, что-то вроде этого:

 // declare a variable of nullable function type: var onSomeActionListener: ((Int, String) -> Unit)? = null // declare higher-order function: fun setOnSomeActionListener(listener: (Int, String) -> Unit) { onSomeActionListener = listener } // set listener: val listener: (Int, String) -> Unit = { p1, p2 -> { /* some stuff */ } } setOnSomeActionListener(listener) // or in one line: setOnSomeActionListener { p1, p2 -> { /* some stuff */ } } 

Для получения дополнительной информации: Функции более высокого порядка и Lambdas

Как насчет определения функции, которая принимает функцию и возвращает интерфейс?

 fun makeOnSomeActionListener(f: (Int,String) -> Unit) = object : OnSomeActionListener { override fun onSomeAction(parameter1: Int, parameter2: String) = f(parameter1, parameter2) } 

Интерфейс делегирует свою работу f .

Тогда вы можете написать

 val thing = SomeClass() thing.setOnSomeActionListener(makeOnSomeActionLisener { parameter1, parameter2 -> // Do stuff here })