Котлин: как передать функцию в качестве параметра другой?

141

Данная функция foo:

fun foo(m: String, bar: (m: String) -> Unit) {
    bar(m)
}

Мы можем сделать:

foo("a message", { println("this is a message: $it") } )
//or 
foo("a message")  { println("this is a message: $it") }

Теперь предположим, что у нас есть следующая функция:

fun buz(m: String) {
   println("another message: $m")
}

Есть ли способ передать «buz» в качестве параметра «foo»? Что-то вроде:

foo("a message", buz)
mhshams
источник

Ответы:

200

Используйте ::для обозначения ссылки на функцию, а затем:

fun foo(m: String, bar: (m: String) -> Unit) {
    bar(m)
}

// my function to pass into the other
fun buz(m: String) {
    println("another message: $m")
}

// someone passing buz into foo
fun something() {
    foo("hi", ::buz)
}

Начиная с Kotlin 1.1 , теперь вы можете использовать функции, являющиеся членами класса (« Связанные вызываемые ссылки »), добавив к оператору ссылки на функцию префикс экземпляра:

foo("hi", OtherClass()::buz)

foo("hi", thatOtherThing::buz)

foo("hi", this::buz)
Джейсон Минард
источник
1
пожалуйста, поправьте меня, если я ошибаюсь, но кажется, что таким образом можно передавать только функции верхнего уровня (т.е. не принадлежащие классу); методы класса не могут :-(
Jaja Harris
7
Ссылка на член может быть передана, но в данном случае это будет функция с двумя параметрами, при этом для первого параметра требуется экземпляр класса. Лучше всего обернуть функцию-член лямбда-выражением, закрывающим класс. Предполагая, что все вышеперечисленное было в классе: fun something() { foo("hi", { buz(it) }) }
Джейсон Минард
1
Кажется, что функции, которые принадлежат классу, могут быть переданы, если вы работаете в том же классе, что и в kotlin 1.1, с которым вы можете делать этоthis::function
Джо Махер
1
Кроме того, если вы хотите сделать параметр функции допускающим значение NULL, просто заключите объявление типа в круглые скобки с вопросительным знаком в конце. напримерbar: ((m: String) -> Unit)?
Aba
1
@MartyMiller, они не такие. Параметр mпредназначен для foo, другой параметр - это имя параметра типа функции, переданное функции в переменной bar.
Джейсон Минард
12

О функции-члене как параметре:

  1. Класс Kotlin не поддерживает статическую функцию-член, поэтому функцию-член нельзя вызывать как: Operator :: add (5, 4)
  2. Следовательно, функцию-член нельзя использовать так же, как функцию первого класса.
  3. Полезный подход - заключить функцию в лямбду. Это не элегантно, но, по крайней мере, работает.

код:

class Operator {
    fun add(a: Int, b: Int) = a + b
    fun inc(a: Int) = a + 1
}

fun calc(a: Int, b: Int, opr: (Int, Int) -> Int) = opr(a, b)
fun calc(a: Int, opr: (Int) -> Int) = opr(a)

fun main(args: Array<String>) {
    calc(1, 2, { a, b -> Operator().add(a, b) })
    calc(1, { Operator().inc(it) })
}
Руй Чжоу
источник
4
В текущем Kotlin теперь вы можете использовать функцию-член в качестве ссылки. Теперь вы должны обновить этот ответ.
Джейсон Минард
Путем определения функций в коде вызова сопутствующего объекта становится немного лучше, и каждый раз не создается новый экземпляр Operator. Это выглядело бы как статичное развлечение на Java
CAB
Отличное решение, это единственный способ, который, как я обнаружил, работает, когда функция находится в классе. Я не считаю это уродливым, но красивым, почти похоже на переменную шаблона angular, но для кода.
gunslingor
4

Котлин 1.1

использовать ::для справки.

лайк

    foo(::buz) // calling buz here

    fun buz() {
        println("i am called")
    }
Соединяя жизнь с Android
источник
4

Просто используйте "::" перед именем метода

fun foo(function: () -> (Unit)) {
   function()
}

fun bar() {
    println("Hello World")
}

foo(::bar) Выход :Hello World

erluxman
источник
этот код не компилируется. "function ()" требует параметра
Джузеппе Джакоппо 08
1

Если вы хотите передать сеттер и геттер методы.

private fun setData(setValue: (Int) -> Unit, getValue: () -> (Int)) {
    val oldValue = getValue()
    val newValue = oldValue * 2
    setValue(newValue)
}

Использование:

private var width: Int = 1

setData({ width = it }, { width })
CoolMind
источник
1

Ответ Джейсона Минарда хороший. Этого также можно добиться с помощью файла lambda.

fun foo(m: String, bar: (m: String) -> Unit) {
    bar(m)
}

val buz = { m: String ->
    println("another message: $m")
}

Что можно назвать с помощью foo("a message", buz).

Вы также можете сделать его более СУХИМ, используя файл typealias.

typealias qux = (m: String) -> Unit

fun foo(m: String, bar: qux) {
    bar(m)
}

val buz: qux = { m ->
    println("another message: $m")
}
flesk
источник
0

Другой пример:

 fun foo(x:Int, Multiply: (Int) -> (Int)) {
    println(Multiply(x))
 }
 fun bar(x:Int):Int{
    return  x * x
 }
 foo(10, ::bar)
Эрик К.
источник
-7

Первоклассные функции в настоящее время не поддерживаются в Kotlin. Были споры о том, стоит ли добавить эту функцию. Я лично считаю, что должны.

ajselvig
источник