Kotlin: Wie übergebe ich eine Funktion als Parameter an eine andere?

140

Gegebene Funktion foo:

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

Wir können tun:

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

Nehmen wir nun an, wir haben die folgende Funktion:

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

Gibt es eine Möglichkeit, "buz" als Parameter an "foo" zu übergeben? Etwas wie:

foo("a message", buz)
mhshams
quelle

Antworten:

198

Verwenden Sie ::diese Option, um eine Funktionsreferenz zu kennzeichnen, und dann:

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

Seit Kotlin 1.1 können Sie jetzt Funktionen verwenden, die Klassenmitglieder sind (" Bound Callable References "), indem Sie dem Funktionsreferenzoperator die Instanz voranstellen :

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

foo("hi", thatOtherThing::buz)

foo("hi", this::buz)
Jayson Minard
quelle
1
Bitte korrigieren Sie mich, wenn ich falsch liege, aber es scheint, dass nur Funktionen der obersten Ebene (dh nicht zu einer Klasse gehören) auf diese Weise übergeben werden können. Klassenmethoden können nicht :-(
Jaja Harris
7
Eine Mitgliedsreferenz kann herumgereicht werden, aber dies wäre in diesem Fall eine 2-Parameter-Funktion, wobei der erste Parameter eine Instanz der Klasse erfordert. Eine bessere Möglichkeit besteht darin, die Elementfunktion mit einem Lambda zu versehen, das die Klasse geschlossen hat. Angenommen, alle oben genannten waren in einer Klasse: fun something() { foo("hi", { buz(it) }) }
Jayson Minard
1
Es scheint, dass Funktionen, die zu einer Klasse gehören, übergeben werden können, wenn Sie innerhalb derselben Klasse arbeiten. Ab Kotlin 1.1 können Sie dies mitthis::function
Joe Maher
1
Wenn Sie den Funktionsparameter auf Null setzen möchten, setzen Sie die Typdeklaration in Klammern mit einem Fragezeichen am Ende. zBbar: ((m: String) -> Unit)?
Aba
1
@MartyMiller sind sie nicht. Der Parameter mist für foo, der andere Parameter ist der Parametername des Funktionstyps, der in der Variablenleiste an die Funktion übergeben wird.
Jayson Minard
12

Informationen zur Elementfunktion als Parameter:

  1. Die Kotlin-Klasse unterstützt keine statische Elementfunktion, daher kann die Elementfunktion nicht wie folgt aufgerufen werden: Operator :: add (5, 4)
  2. Daher kann die Member-Funktion nicht wie die First-Class-Funktion verwendet werden.
  3. Ein nützlicher Ansatz besteht darin, die Funktion mit einem Lambda zu versehen. Es ist nicht elegant, aber es funktioniert zumindest.

Code:

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
quelle
4
In aktuellen Kotlin Sie jetzt kann eine Memberfunktion als Referenz verwenden. Sie sollten diese Antwort jetzt aktualisieren.
Jayson Minard
Durch das Definieren von Funktionen im Begleitobjekt wird der Aufrufcode etwas besser und es wird nicht jedes Mal eine neue Instanz von Operator erstellt. Dies würde wie ein statischer Spaß in Java aussehen
CAB
Tolle Lösung, dies ist der einzige Weg, den ich gefunden habe, der funktioniert, wenn sich die Funktion in einer Klasse befindet. Ich halte es nicht für hässlich, aber schön, sieht fast wie eine eckige Vorlagenvariable aus, aber für Code.
Revolverheld oder
4

Kotlin 1.1

verwenden , ::um Referenzmethode.

mögen

    foo(::buz) // calling buz here

    fun buz() {
        println("i am called")
    }
Das Leben mit Android verbinden
quelle
4

Verwenden Sie einfach "::" vor dem Methodennamen

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

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

foo(::bar) Ausgabe :Hello World

Erluxman
quelle
Dieser Code wird nicht kompiliert. "function ()" erfordert einen Parameter
Giuseppe Giacoppo
1

Wenn Sie Setter- und Getter- Methoden übergeben möchten .

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

Verwendung:

private var width: Int = 1

setData({ width = it }, { width })
CoolMind
quelle
1

Jason Minards Antwort ist gut. Dies könnte auch mit a erreicht werden lambda.

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

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

Welches kann mit aufgerufen werden foo("a message", buz).

Sie können dies auch etwas trockener machen, indem Sie a verwenden typealias.

typealias qux = (m: String) -> Unit

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

val buz: qux = { m ->
    println("another message: $m")
}
flesk
quelle
0

Ein anderes Beispiel:

 fun foo(x:Int, Multiply: (Int) -> (Int)) {
    println(Multiply(x))
 }
 fun bar(x:Int):Int{
    return  x * x
 }
 foo(10, ::bar)
Erik K.
quelle
-7

Erstklassige Funktionen werden derzeit in Kotlin nicht unterstützt. Es gab Debatten darüber, ob dies eine gute Funktion wäre. Ich persönlich denke, sie sollten.

ajselvig
quelle