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)

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)

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

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

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

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

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

foo(::bar) Sortie :Hello World


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

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

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)

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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.