Kotlin: como passar uma função como parâmetro para outra?

141

Função dada foo:

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

Nós podemos fazer:

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

Agora, digamos que temos a seguinte função:

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

Existe uma maneira de eu passar "buz" como parâmetro para "foo"? Algo como:

foo("a message", buz)
mhshams
fonte

Respostas:

199

Use ::para significar uma referência de função e, em seguida:

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 o Kotlin 1.1, agora você pode usar funções que são membros da classe (" Referências vinculativas vinculadas "), prefixando o operador de referência da função com a instância:

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

foo("hi", thatOtherThing::buz)

foo("hi", this::buz)
Jayson Minard
fonte
1
corrija-me se estiver errado, mas parece que apenas as funções de nível superior (ou seja, não pertencem a uma classe) podem ser aprovadas dessa maneira; métodos de classe não pode :-(
Jaja Harris
7
Uma referência de membro pode ser transmitida, mas essa seria uma função de 2 parâmetros nesse caso, com o primeiro parâmetro exigindo uma instância da classe. Uma maneira melhor é agrupar a função de membro com um lambda fechado na classe. Assumindo que tudo o que foi exposto acima foi em uma classe: fun something() { foo("hi", { buz(it) }) }
Jayson Minard 25/12
1
Parece que as funções que fazem pertencem a uma classe pode ser passada em se você estiver operando dentro da mesma classe, a partir de Kotlin 1.1 você pode fazê-lo comthis::function
Joe Maher
1
Além disso, se você deseja tornar o parâmetro da função anulável, basta colocar a declaração de tipo entre parênteses com um ponto de interrogação no final. por exemplobar: ((m: String) -> Unit)?
Aba
1
@MartyMiller eles não são. O parâmetro mé para foo, o outro parâmetro é o nome do parâmetro do tipo de função passado na barra de variáveis ​​para a função.
Jayson Minard 29/09
12

Sobre a função de membro como parâmetro:

  1. A classe Kotlin não suporta a função membro estática, portanto, a função membro não pode ser chamada como: Operator :: add (5, 4)
  2. Portanto, a função de membro não pode ser usada da mesma forma que a função de primeira classe.
  3. Uma abordagem útil é agrupar a função com um lambda. Não é elegante, mas pelo 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
fonte
4
No Kotlin atual, agora você pode usar uma função de membro como referência. Agora você deve atualizar esta resposta.
Jayson Minard
A definição de funções no código de chamada do objeto complementar melhora um pouco e nenhuma nova instância do Operator é criada a cada vez. Isso seria parecido com um divertimento estática em Java
CAB
Ótima solução, essa é a única maneira que descobri que funciona quando a função está em uma classe. Eu não considero feio, mas bonito, quase parece uma variável de modelo angular, mas para código.
gunslingor
4

Kotlin 1.1

use ::para referenciar o método.

gostar

    foo(::buz) // calling buz here

    fun buz() {
        println("i am called")
    }
Conectando a vida com o Android
fonte
4

Basta usar "::" antes do nome do método

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

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

foo(::bar) Saída :Hello World

erluxman
fonte
esse código não compila. "function ()" requer um parâmetro
Giuseppe Giacoppo 08/04/19
1

Se você deseja passar os métodos 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 })
CoolMind
fonte
1

A resposta de Jason Minard é boa. Isso também pode ser alcançado usando a lambda.

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

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

Qual pode ser chamado com foo("a message", buz).

Você também pode tornar isso um pouco mais 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
fonte
0

Outro exemplo:

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

Atualmente, as funções de primeira classe não são suportadas no Kotlin. Houve um debate sobre se esse seria um bom recurso a ser adicionado. Eu pessoalmente acho que eles deveriam.

ajselvig
fonte