Kotlin: come passare una funzione come parametro a un'altra?


141

Funzione data foo:

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

Possiamo fare:

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

Ora, supponiamo che abbiamo la seguente funzione:

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

C'è un modo in cui posso passare "buz" come parametro a "pippo"? Qualcosa di simile a:

foo("a message", buz)

Risposte:


199

Utilizzare ::per indicare un riferimento alla funzione, quindi:

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

Da Kotlin 1.1 ora puoi usare le funzioni che sono membri della classe (" Riferimenti richiamabili associati "), prefissando l'operatore di riferimento della funzione con l'istanza:

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

foo("hi", thatOtherThing::buz)

foo("hi", this::buz)

1
per favore correggimi se sbaglio ma sembra che solo le funzioni di livello superiore (cioè non appartengano a una classe) possano essere passate in questo modo; i metodi di classe non possono :-(
Jaja Harris il

7
È possibile passare un riferimento membro, ma in questo caso sarebbe una funzione a 2 parametri con il primo parametro che richiede un'istanza della classe. Un modo migliore è avvolgere la funzione membro con un lambda che si è chiuso sulla classe. Supponendo che tutto quanto sopra fosse in una classe: fun something() { foo("hi", { buz(it) }) }
Jayson Minard il

1
Sembra che funzioni che appartengono a una classe possano essere passate se stai operando all'interno della stessa classe, a partire da kotlin 1.1 puoi farlo conthis::function
Joe Maher

1
Inoltre, se si desidera rendere nullo il parametro della funzione, racchiudere la dichiarazione del tipo tra parentesi con un punto interrogativo alla fine. ad es.bar: ((m: String) -> Unit)?
Aba,

1
@MartyMiller non lo sono. Il parametro mè per foo, l'altro parametro è il nome del parametro del tipo di funzione passato nella barra variabile alla funzione.
Jayson Minard,

12

Informazioni sulla funzione membro come parametro:

  1. La classe Kotlin non supporta la funzione membro statica, quindi la funzione membro non può essere invocata come: Operatore :: add (5, 4)
  2. Pertanto, la funzione membro non può essere utilizzata come la funzione di prima classe.
  3. Un approccio utile è avvolgere la funzione con una lambda. Non è elegante ma almeno funziona.

codice:

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
Nell'attuale Kotlin, ora puoi usare una funzione membro come riferimento. Ora dovresti aggiornare questa risposta.
Jayson Minard,

Definendo le funzioni nell'oggetto associato, il codice chiamante diventa un po 'migliore e non viene creata ogni nuova istanza di Operator. Sembrerebbe un divertimento statico in Java
CAB,

Ottima soluzione, questo è l'unico modo che ho trovato che funziona quando la funzione è in una classe. Non lo considero brutto ma bello, sembra quasi una variabile di modello angolare ma per il codice.
pistolero o


4

Basta usare "::" prima del nome del metodo

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

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

foo(::bar) Uscita :Hello World


questo codice non viene compilato. "function ()" richiede un parametro
Giuseppe Giacoppo


1

Se si desidera passare i metodi setter e getter .

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

Uso:

private var width: Int = 1

setData({ width = it }, { width })

1

La risposta di Jason Minard è buona. Questo potrebbe anche essere ottenuto usando a lambda.

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

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

Che può essere chiamato con foo("a message", buz).

Puoi anche renderlo un po 'più ASCIUTTO usando a typealias.

typealias qux = (m: String) -> Unit

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

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

0

Un altro esempio:

 fun foo(x:Int, Multiply: (Int) -> (Int)) {
    println(Multiply(x))
 }
 fun bar(x:Int):Int{
    return  x * x
 }
 foo(10, ::bar)

-7

Le funzioni di prima classe non sono attualmente supportate in Kotlin. Si è discusso se questa sarebbe una buona caratteristica da aggiungere. Personalmente penso che dovrebbero.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.