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)

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)

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

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

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

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

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

foo(::bar) Salida :Hello World


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

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

0

Otro ejemplo:

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

-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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.