Kotlin: ¿cómo pasar una función como parámetro a otra?

141

Dada la función foo:

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

Podemos hacer:

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

Ahora, digamos que tenemos la siguiente función:

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

¿Hay alguna manera de pasar "buz" como parámetro a "foo"? Algo como:

foo("a message", buz)
mhshams
fuente

Respuestas:

199

Use ::para significar una referencia de función, y luego:

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

Desde Kotlin 1.1 ahora puede usar funciones que son miembros de la clase (" Referencias invocables enlazadas "), al prefijar el operador de referencia de función con la instancia:

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

foo("hi", thatOtherThing::buz)

foo("hi", this::buz)
Jayson Minard
fuente
1
corríjame si me equivoco, pero parece que solo las funciones de nivel superior (es decir, no pertenecen a una clase) se pueden pasar de esta manera; los métodos de clase no pueden :-(
Jaja Harris
77
Se puede pasar una referencia de miembro, pero esa sería una función de 2 parámetros en este caso con el primer parámetro que requiere una instancia de la clase. Una mejor manera es envolver la función miembro con una lambda que se cerró en la clase. Asumiendo que todo lo anterior estaba en una clase: fun something() { foo("hi", { buz(it) }) }
Jayson Minard
1
Parece que las funciones que pertenecen a una clase pueden pasarse si está operando dentro de la misma clase, a partir de kotlin 1.1 puede hacerlothis::function
Joe Maher
1
Además, si desea hacer que el parámetro de la función sea anulable, simplemente envuelva la declaración de tipo entre paréntesis con un signo de interrogación al final. por ejemplobar: ((m: String) -> Unit)?
Aba
1
@MartyMiller no lo son. El parámetro mes para foo, el otro parámetro es el nombre del parámetro del tipo de función pasado en la barra variable a la función.
Jayson Minard
12

Sobre la función miembro como parámetro:

  1. La clase Kotlin no admite la función miembro estática, por lo que la función miembro no se puede invocar como: Operator :: add (5, 4)
  2. Por lo tanto, la función miembro no se puede usar igual que la función de Primera clase.
  3. Un enfoque útil es envolver la función con una lambda. No es elegante, pero al menos está funcionando.

código:

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
fuente
44
En Kotlin actual, ahora puede usar una función miembro como referencia. Ahora debería actualizar esta respuesta.
Jayson Minard
Al definir las funciones en el código de llamada del objeto complementario, se mejora un poco y no se crea ninguna instancia nueva de Operador cada vez. Esto parecería una diversión estática en Java
CAB
Gran solución, esta es la única forma en que encontré que funciona cuando la función está en una clase. No lo considero feo pero hermoso, casi parece una variable de plantilla angular, pero para el código.
gunslingor
4

Kotlin 1.1

utilizar ::como método de referencia.

me gusta

    foo(::buz) // calling buz here

    fun buz() {
        println("i am called")
    }
Conectando la vida con Android
fuente
4

Simplemente use "::" antes del nombre del método

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

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

foo(::bar) Salida :Hello World

erluxman
fuente
Este código no se compila. "function ()" requiere un parámetro
Giuseppe Giacoppo
1

Si desea pasar los métodos setter y 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 })
CoolMind
fuente
1

La respuesta de Jason Minard es buena. Esto también podría lograrse usando a lambda.

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

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

Que se puede llamar con foo("a message", buz).

También puedes hacer esto un poco más SECO usando a typealias.

typealias qux = (m: String) -> Unit

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

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

Otro ejemplo:

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

Las funciones de primera clase actualmente no son compatibles con Kotlin. Se ha debatido si esta sería una buena característica para agregar. Personalmente creo que deberían hacerlo.

ajselvig
fuente