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)

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)

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

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

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

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

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

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


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

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

0

Outro exemplo:

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

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.