Kotlin: comment passer une fonction en paramètre à une autre?

141

Compte tenu de la fonction foo:

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

Nous pouvons faire:

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

Maintenant, disons que nous avons la fonction suivante:

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

Existe-t-il un moyen de passer "buz" comme paramètre à "foo"? Quelque chose comme:

foo("a message", buz)
mhshams
la source

Réponses:

200

Utilisez ::pour indiquer une référence de fonction, puis:

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

Depuis Kotlin 1.1, vous pouvez désormais utiliser des fonctions qui sont des membres de classe (" Bound Callable References "), en préfixant l'opérateur de référence de fonction avec l'instance:

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

foo("hi", thatOtherThing::buz)

foo("hi", this::buz)
Jayson Minard
la source
1
veuillez me corriger si je me trompe, mais il semble que seules les fonctions de niveau supérieur (c'est-à-dire n'appartiennent pas à une classe) peuvent être passées de cette manière; les méthodes de classe ne peuvent pas :-(
Jaja Harris
7
Une référence de membre peut être transmise, mais ce serait une fonction à 2 paramètres dans ce cas avec le premier paramètre nécessitant une instance de la classe. Un meilleur moyen consiste à envelopper la fonction membre avec un lambda qui se ferme sur la classe. En supposant que tout ce qui précède était dans une classe: fun something() { foo("hi", { buz(it) }) }
Jayson Minard
1
Il semble que les fonctions qui appartiennent à une classe peuvent être transmises si vous utilisez la même classe, à partir de kotlin 1.1, vous pouvez le faire avecthis::function
Joe Maher
1
De plus, si vous souhaitez rendre le paramètre de fonction nullable, mettez simplement la déclaration de type entre parenthèses avec un point d'interrogation à la fin. par exemplebar: ((m: String) -> Unit)?
Aba
1
@MartyMiller ils ne le sont pas. Le paramètre mest pour foo, l'autre paramètre est le nom du paramètre du type de fonction passé en barre de variable à la fonction.
Jayson Minard
12

À propos de la fonction membre en tant que paramètre:

  1. La classe Kotlin ne prend pas en charge la fonction membre statique, donc la fonction membre ne peut pas être appelée comme: Operator :: add (5, 4)
  2. Par conséquent, la fonction membre ne peut pas être utilisée comme la fonction de première classe.
  3. Une approche utile consiste à envelopper la fonction avec un lambda. Ce n'est pas élégant mais au moins ça marche.

code:

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
la source
4
Dans Kotlin actuel, vous pouvez désormais utiliser une fonction membre comme référence. Vous devez maintenant mettre à jour cette réponse.
Jayson Minard
En définissant des fonctions dans le code d'appel d'objet compagnon, obtenez un peu mieux et aucune nouvelle instance d'Operator n'est créée à chaque fois. Cela ressemblerait à un amusement statique en Java
CAB
Excellente solution, c'est le seul moyen que j'ai trouvé qui fonctionne lorsque la fonction est dans une classe. Je ne le considère pas moche mais beau, ressemble presque à une variable de modèle angulaire mais pour le code.
gunslingor
4

Kotlin 1.1

utiliser ::pour référencer la méthode.

comme

    foo(::buz) // calling buz here

    fun buz() {
        println("i am called")
    }
Connecter la vie avec Android
la source
4

Utilisez simplement "::" avant le nom de la méthode

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

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

foo(::bar) Sortie :Hello World

Erluxman
la source
ce code ne compile pas. "function ()" nécessite un paramètre
Giuseppe Giacoppo
1

Si vous voulez passer les méthodes setter et getter .

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

Usage:

private var width: Int = 1

setData({ width = it }, { width })
CoolMind
la source
1

La réponse de Jason Minard est bonne. Cela pourrait également être réalisé en utilisant un fichier lambda.

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

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

Qui peut être appelé avec foo("a message", buz).

Vous pouvez également rendre cela un peu plus SEC en utilisant un fichier typealias.

typealias qux = (m: String) -> Unit

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

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

Un autre exemple:

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

Les fonctions de première classe ne sont actuellement pas prises en charge dans Kotlin. Il y a eu un débat pour savoir si ce serait une bonne fonctionnalité à ajouter. Je pense personnellement qu'ils devraient.

ajselvig
la source