Kotlin: jak przekazać inną funkcję jako parametr?

157

Podana funkcja foo:

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

Możemy zrobić:

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

Teraz powiedzmy, że mamy następującą funkcję:

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

Czy istnieje sposób na przekazanie „buz” jako parametru do „foo”? Coś jak:

foo("a message", buz)
mhshams
źródło

Odpowiedzi:

230

Służy ::do oznaczenia odwołania do funkcji, a następnie:

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)
}

Od wersji Kotlin 1.1 można teraz używać funkcji, które są członkami klasy („ Bound wywoływalne referencje ”), poprzedzając operator odwołania do funkcji instancją:

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

foo("hi", thatOtherThing::buz)

foo("hi", this::buz)
Jayson Minard
źródło
1
proszę poprawić mnie, jeśli się mylę, ale wydaje się, że tylko funkcje najwyższego poziomu (tj. nie należą do klasy) mogą być przekazywane w ten sposób; metody klasowe nie mogą :-(
Jaja Harris
8
Można przekazać odwołanie do elementu członkowskiego, ale w tym przypadku byłaby to funkcja dwuparametrowa, z pierwszym parametrem wymagającym wystąpienia klasy. Lepszym sposobem jest owinięcie funkcji składowej wyrażeniem lambda zamkniętym w klasie. Zakładając, że wszystkie powyższe były w klasie: fun something() { foo("hi", { buz(it) }) }
Jayson Minard
1
Wygląda na to, że funkcje, które należą do klasy, mogą być przekazywane, jeśli działasz w tej samej klasie, od kotlin 1.1 możesz to zrobićthis::function
Joe Maher
1
Ponadto, jeśli chcesz, aby parametr funkcji miał wartość null, po prostu zawiń deklarację typu w nawiasach ze znakiem zapytania na końcu. np.bar: ((m: String) -> Unit)?
Aba
1
@MartyMiller nie są. Parametr mjest dla foo, drugi parametr to nazwa parametru typu funkcji przekazana w zmiennej bar do funkcji.
Jayson Minard
13

O funkcji członkowskiej jako parametrze:

  1. Klasa Kotlin nie obsługuje statycznej funkcji składowej, więc nie można wywołać funkcji składowej, takiej jak: Operator :: add (5, 4)
  2. W związku z tym funkcja członkowska nie może być używana tak samo jak funkcja pierwszej klasy.
  3. Przydatnym podejściem jest zawijanie funkcji za pomocą lambda. Nie jest eleganckie, ale przynajmniej działa.

kod:

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) })
}
Rui Zhou
źródło
4
W bieżącym Kotlin, teraz można użyć funkcji składowej jako odniesienie. Powinieneś teraz zaktualizować tę odpowiedź.
Jayson Minard
Definiując funkcje w kodzie wywołującym obiekt towarzyszący, stają się nieco lepsze i za każdym razem nie jest tworzone nowe wystąpienie operatora. To wyglądałoby na statyczną zabawę w Javie
CAB,
Świetne rozwiązanie, to jedyny sposób, jaki znalazłem, który działa, gdy funkcja znajduje się w klasie. Nie uważam tego za brzydkie, ale piękne, prawie wygląda jak kątowa zmienna szablonu, ale dla kodu.
Gunslingor
6

Po prostu użyj „::” przed nazwą metody

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

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

foo(::bar) Wyjście :Hello World

erluxman
źródło
ten kod nie kompiluje się. Funkcja „function ()” wymaga parametru
Giuseppe Giacoppo
4

Kotlin 1.1

użyj ::do metody referencyjnej.

lubić

    foo(::buz) // calling buz here

    fun buz() {
        println("i am called")
    }
Łączenie życia z Androidem
źródło
2

Jeśli chcesz przekazać metody setter i getter .

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

Stosowanie:

private var width: Int = 1

setData({ width = it }, { width })
CoolMind
źródło
1

Odpowiedź Jasona Minarda jest dobra. Można to również osiągnąć za pomocą pliku lambda.

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

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

Które można zadzwonić foo("a message", buz).

Możesz również uczynić to nieco bardziej SUCHY, używając pliku typealias.

typealias qux = (m: String) -> Unit

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

val buz: qux = { m ->
    println("another message: $m")
}
flesk
źródło
0

Inny przykład:

 fun foo(x:Int, Multiply: (Int) -> (Int)) {
    println(Multiply(x))
 }
 fun bar(x:Int):Int{
    return  x * x
 }
 foo(10, ::bar)
Erik K.
źródło
0

Możesz to również zrobić bezpośrednio przy użyciu lambdy, jeśli jest to jedyne miejsce, w którym używasz tej funkcji

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

foo("a message") {
    m: String -> println("another message: $m")
}
//Outputs: another message: a message
kooskoos
źródło
-7

Funkcje pierwszej klasy nie są obecnie obsługiwane w Kotlinie. Odbyła się debata na temat tego, czy warto byłoby to dodać. Osobiście uważam, że powinni.

ajselvig
źródło