
Introduction aux lambdas : écrire des fonctions courtes
Découvrez les expressions lambda en Kotlin : des fonctions anonymes concises pour passer du comportement comme une valeur. Apprenez la syntaxe et les cas d'usage.
Introduction : au-delà des fonctions nommées
Jusqu'à présent, nous avons défini des fonctions en utilisant le mot-clé `fun` suivi d'un nom, de paramètres et d'un corps. Ces fonctions nommées sont parfaites pour encapsuler des logiques réutilisables et bien définies. Cependant, il existe de nombreuses situations où nous avons besoin de définir une petite logique (un comportement) de manière très concise, souvent pour la passer en argument à une autre fonction, sans avoir besoin de lui donner un nom formel.
Imaginez devoir fournir une règle de tri personnalisée, spécifier une action à effectuer sur chaque élément d'une liste, ou définir un callback à exécuter lorsqu'un événement se produit. Créer une fonction nommée distincte pour chaque petite tâche de ce genre peut alourdir considérablement le code et le rendre moins lisible.
C'est là qu'interviennent les expressions lambda (ou simplement "lambdas"). Les lambdas sont essentiellement des fonctions anonymes que vous pouvez définir directement là où vous en avez besoin, et que vous pouvez traiter comme des valeurs (les assigner à des variables, les passer en arguments, les retourner depuis d'autres fonctions). Elles sont un pilier de la programmation fonctionnelle et un outil extrêmement puissant et idiomatique en Kotlin, notamment pour travailler avec les collections.
Définition : qu'est-ce qu'une expression lambda ?
Une expression lambda est une manière concise de définir une fonction sans utiliser le mot-clé `fun` et sans lui donner de nom. C'est une "fonction littérale". Elle représente un bloc de code (un comportement) qui peut être exécuté plus tard.
La caractéristique clé des lambdas est qu'elles peuvent être traitées comme n'importe quelle autre valeur en Kotlin. Vous pouvez stocker une lambda dans une variable, la passer comme argument à une fonction qui attend une fonction en paramètre (ces fonctions sont appelées "fonctions d'ordre supérieur" ou "higher-order functions"), ou même la retourner comme résultat d'une autre fonction.
Pensez à une lambda comme à une petite recette ou une instruction que vous écrivez sur un bout de papier sans lui donner de titre formel. Vous pouvez utiliser cette instruction immédiatement, la garder pour plus tard, ou la donner à quelqu'un d'autre pour qu'il l'exécute.
La syntaxe essentielle : `{ paramètres -> corps }`
La syntaxe de base d'une expression lambda en Kotlin est simple et reconnaissable : elle est toujours entourée d'accolades `{}`.
A l'intérieur des accolades, la structure est généralement la suivante :
`{ parametre1: Type1, parametre2: Type2, ... -> corps_de_la_lambda }`
Décortiquons cette syntaxe :
- `{ }` : Les accolades délimitent le début et la fin de l'expression lambda. Elles sont obligatoires.
- `parametre1: Type1, parametre2: Type2, ...` : C'est la liste optionnelle des paramètres que la lambda accepte en entrée. Chaque paramètre est déclaré avec son nom et son type, séparés par des virgules (comme dans une fonction nommée).
- `->` : La flèche sépare la liste des paramètres (à gauche) du corps de la lambda (à droite). Elle est nécessaire s'il y a des paramètres déclarés. S'il n'y a aucun paramètre, la flèche et la partie gauche peuvent être omises.
- `corps_de_la_lambda` : C'est le bloc de code qui contient les instructions à exécuter lorsque la lambda est appelée. Comme pour une fonction standard, la dernière expression évaluée dans le corps est considérée comme la valeur de retour de la lambda (si elle en retourne une).
Voici quelques exemples de lambdas simples :
// 1. Lambda sans paramètres, ne retournant rien (Unit)
val printAction: () -> Unit = { println("Action exécutée !") }
// 2. Lambda avec un paramètre (Int), ne retournant rien (Unit)
val printNumber: (Int) -> Unit = { number: Int -> println("Nombre reçu: $number") }
// 3. Lambda avec deux paramètres (Int, Int), retournant leur somme (Int)
val sumLambda: (Int, Int) -> Int = { a: Int, b: Int ->
println("Calcul de la somme...") // Peut contenir plusieurs instructions
a + b // La dernière expression est la valeur de retour
}
// 4. Lambda sans paramètres, retournant une String
val getGreeting: () -> String = { "Bonjour depuis la lambda !" }
// Pour appeler une lambda stockée dans une variable :
fun main() {
printAction() // Exécute la première lambda
printNumber(42) // Exécute la seconde avec 42
val result = sumLambda(10, 5) // Exécute la troisième
println("Résultat somme: $result") // Affiche 15
println(getGreeting()) // Exécute et affiche le message de la quatrième
}Notez les types des variables (`() -> Unit`, `(Int) -> Unit`, etc.). C'est la notation pour les types "fonction" en Kotlin, indiquant les types des paramètres et le type de retour.
Simplifications : inférence de type et `it`
Kotlin rend l'écriture des lambdas encore plus concise grâce à plusieurs mécanismes d'inférence :
- Inférence des types de paramètres : Très souvent, le compilateur peut déduire les types des paramètres de la lambda à partir du contexte où elle est utilisée (par exemple, si elle est passée à une fonction qui attend spécifiquement une lambda de type `(String) -> Boolean`). Dans ce cas, vous pouvez omettre les types des paramètres.
- Inférence du type de retour : Le compilateur infère automatiquement le type de retour de la lambda en se basant sur la dernière expression dans son corps.
- Paramètre implicite `it` : Si une lambda n'a qu'un seul paramètre et que son type peut être inféré par le compilateur, vous n'avez même pas besoin de déclarer ce paramètre explicitement. Vous pouvez y faire référence directement en utilisant le nom implicite `it`.
Voyons comment cela simplifie nos exemples précédents lorsque le contexte permet l'inférence :
// Supposons une fonction qui attend une lambda (Int) -> Unit
fun processNumber(action: (Int) -> Unit) {
action(100)
}
// Supposons une fonction qui attend une lambda (Int, Int) -> Int
fun combineNumbers(combiner: (Int, Int) -> Int): Int {
return combiner(5, 7)
}
fun main() {
// 1. Inférence des types de paramètres
val sumLambdaConcise: (Int, Int) -> Int = { a, b -> a + b } // Types a: Int, b: Int omis
println(sumLambdaConcise(3, 4)) // Affiche 7
// 2. Utilisation de 'it' pour le paramètre unique
processNumber { println("Traitement de $it !") } // 'it' représente l'unique paramètre Int
// C'est équivalent à: processNumber { number -> println("Traitement de $number !") }
// 3. Exemple combiné
val combinedResult = combineNumbers { x, y -> x * y } // Types de x, y et retour (Int) inférés
println("Résultat combiné: $combinedResult") // Affiche 35
}L'utilisation de `it` est extrêmement fréquente en Kotlin et rend le code de manipulation de collections particulièrement succinct.
Les lambdas comme valeurs : passer du comportement
Le concept fondamental est que les lambdas sont des valeurs. Cela signifie que vous pouvez les manipuler comme n'importe quelle autre donnée :
- Assigner à une variable/valeur : Comme vu dans les exemples (`val sumLambda = { ... }`).
- Passer en argument à une fonction : C'est l'usage le plus courant. Les fonctions qui acceptent d'autres fonctions (ou lambdas) comme paramètres sont appelées fonctions d'ordre supérieur.
- Retourner depuis une fonction : Une fonction peut construire et retourner une lambda.
La capacité de passer des lambdas comme arguments est ce qui rend les opérations sur les collections en Kotlin si puissantes. Vous passez une lambda qui définit comment filtrer, comment transformer, ou quoi faire pour chaque élément.
fun main() {
val numbers = listOf(1, 2, 3, 4, 5, 6)
// Exemple 1: Passer une lambda à forEach
println("Affichage avec forEach:")
numbers.forEach { println(" Item: $it") } // Lambda définit l'action pour chaque élément
// Exemple 2: Passer une lambda à filter
val evenNumbers = numbers.filter { it % 2 == 0 } // Lambda définit la condition de filtrage
println("Nombres pairs: $evenNumbers") // Affiche: Nombres pairs: [2, 4, 6]
// Exemple 3: Passer une lambda à map
val squaredNumbers = numbers.map { it * it } // Lambda définit la transformation
println("Nombres au carré: $squaredNumbers") // Affiche: Nombres au carré: [1, 4, 9, 16, 25, 36]
}Nous détaillerons ces fonctions de collection (`forEach`, `filter`, `map`, etc.) dans la section suivante, mais cette introduction aux lambdas est essentielle pour comprendre comment elles fonctionnent.
Récapitulatif : l'essence des lambdas
Les expressions lambda sont un outil fondamental en Kotlin :
- Ce sont des fonctions anonymes définies de manière concise.
- Syntaxe clé : `{ paramètres -> corps }`.
- Elles sont traitées comme des valeurs : assignables, passables en argument, retournables.
- Kotlin utilise l'inférence de type pour simplifier leur écriture.
- Pour les lambdas à paramètre unique (avec type inféré), le paramètre implicite `it` peut être utilisé.
- Elles sont cruciales pour le style de programmation fonctionnel et omniprésentes dans les opérations sur les collections.
Comprendre les lambdas ouvre la porte à une écriture de code Kotlin beaucoup plus expressive, concise et puissante.