CodeGym /Cours /JAVA 25 SELF /Expressions lambda: syntaxe et portée

Expressions lambda: syntaxe et portée

JAVA 25 SELF
Niveau 48, Leçon 0
Disponible

1. Introduction

Soyons honnêtes: écrire des classes anonymes pour une ou deux lignes de code — c’est comme louer un énorme camion pour transporter une seule brioche de la boulangerie au magasin.

Par exemple, si vous voulez trier une liste de chaînes par longueur, avant Java 8 il fallait écrire ceci :

List<String> list = Arrays.asList("pomme", "banane", "kiwi");
Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

La tâche est simple, mais le code — prend la moitié de l’écran. C’est particulièrement agaçant quand ces opérations sont nombreuses : le code devient « bruyant », l’essentiel se perd. Les développeurs ont souffert, puis ils ont inventé les expressions lambda. Nous les avons déjà un peu étudiées, maintenant nous allons réviser et approfondir nos connaissances.

Qu’est-ce qu’une expression lambda

Une expression lambda est une forme compacte d’implémentation d’une interface fonctionnelle, c’est-à-dire une interface avec une seule méthode abstraite (par exemple, Comparator, Runnable, Consumer et bien d’autres).

Plus simplement, une expression lambda vous permet d’écrire une fonction « à la volée », exactement là où elle est nécessaire, sans déclarer de classe ou de méthode séparée.

Syntaxe générale :

(paramètres) -> { corps }

Exemples :

  • (a, b) -> a + b — fonction d’addition de deux nombres
  • x -> x * x — fonction d’élévation au carré
  • () -> System.out.println("Hello!") — fonction sans paramètres

Lien avec les interfaces fonctionnelles :
Une expression lambda peut toujours être affectée à une variable de type interface fonctionnelle ou passée en argument à une méthode qui attend une telle interface.

2. Syntaxe des expressions lambda

Sans paramètres

Runnable r = () -> System.out.println("Bonjour, le monde!");
r.run(); // Affichera: Bonjour, le monde!

Un paramètre
Si le paramètre est unique, les parenthèses peuvent être omises :

Consumer<String> print = s -> System.out.println(s);
print.accept("Java, c'est génial!");

Plusieurs paramètres
Les parenthèses sont obligatoires :

Comparator<String> cmp = (a, b) -> a.length() - b.length();

Corps d’une seule expression
Si le corps se compose d’une seule expression, les accolades et return ne sont pas nécessaires :

Function<Integer, Integer> square = x -> x * x;
System.out.println(square.apply(5)); // 25

Corps sous forme de bloc
Si vous avez besoin de plusieurs instructions, utilisez des accolades et return (s’il y a une valeur de retour) :

Function<Integer, Integer> abs = x -> {
    if (x < 0) {
        return -x;
    }
    return x;
};
System.out.println(abs.apply(-3)); // 3

Types de paramètres
Le plus souvent, il n’est pas nécessaire d’indiquer les types des paramètres — le compilateur les déduit du contexte. Mais si vous le souhaitez, vous pouvez les préciser explicitement :

Comparator<String> cmp = (String a, String b) -> a.length() - b.length();

Lambda sans valeur de retour
Si l’interface retourne void, écrivez simplement les instructions :

list.forEach(s -> System.out.println("Élément: " + s));

Tableau: Variantes de syntaxe des expressions lambda

Ce que l’on fait Exemple Commentaire
Sans paramètres
() -> System.out.println("Hi")
Par exemple, pour Runnable
Un paramètre
x -> x * x
Parenthèses facultatives
Plusieurs paramètres
(a, b) -> a + b
Parenthèses obligatoires
Une seule expression
x -> x + 1
Sans return ni accolades
Bloc de code
x -> { int y = x + 1; return y * 2; }
Avec return s’il y a un résultat

3. Utilisation: où et comment utiliser les expressions lambda

Les expressions lambda sont surtout utilisées là où il faut passer un « comportement » — une fonction — en argument. Cela a été une véritable révolution pour les collections, les flux (Stream API), les événements et bien d’autres choses.

Tri d’une liste

Avant Java 8 :

list.sort(new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

Avec une expression lambda :

list.sort((a, b) -> a.length() - b.length());

Création d’un thread (Thread)

Thread t = new Thread(() -> System.out.println("Le thread est démarré!"));
t.start();

Traitement des collections

list.forEach(s -> System.out.println(s.toUpperCase()));

Filtrage d’une liste

List<String> longWords = list.stream()
    .filter(s -> s.length() > 5)
    .collect(Collectors.toList());

Exemple: faisons évoluer notre application pédagogique

Supposons que nous avons une liste d’utilisateurs :

List<String> users = Arrays.asList("Alice", "Bob", "Charlie");

Afficher tous les utilisateurs dont le nom dépasse 4 caractères :

users.stream()
    .filter(name -> name.length() > 4)
    .forEach(name -> System.out.println("Utilisateur: " + name));

4. Portée des variables dans les expressions lambda

Les expressions lambda peuvent utiliser des variables de la méthode englobante, mais il y a des subtilités !

Variables et lambdas

En Java, une lambda peut « capturer » uniquement des variables qui ne changent plus après leur initialisation. Si la variable est déclarée avec final, c’est évident. Mais même si le mot final n’est pas écrit, le compilateur vérifie lui-même si la valeur change ou non. Si elle ne change pas, il la considère comme « comme si elle était finale » et l’autorise sans problème dans la lambda.

Exemple :

int minLength = 4; // la valeur ne change nulle part
users.forEach(name -> {
    if (name.length() > minLength) {
        System.out.println(name);
    }
});

Ici, tout fonctionne parce que minLength reste le même nombre.

Mais si, après l’avoir utilisée dans la lambda, vous essayez de réaffecter minLength, vous obtiendrez une erreur de compilation :

int minLength = 4;
users.forEach(name -> {
    if (name.length() > minLength) {
        System.out.println(name);
    }
});
minLength = 10; // Erreur! La lambda a déjà "capturé" la valeur

En substance, la règle est très simple : toute variable utilisée dans une lambda doit être immuable.

Différence avec les classes anonymes

Dans les classes anonymes et les lambdas, les variables de la méthode externe fonctionnent de la même manière : seulement final/effectivement final.

MAIS !
Dans une expression lambda, this fait référence à l’objet externe (l’instance actuelle de la classe), alors que dans une classe anonyme — à l’instance de la classe anonyme. C’est important si, à l’intérieur d’une lambda, vous accédez aux champs ou méthodes de la classe courante.

Exemple :

public class Example {
    String name = "Classe externe";

    void demo() {
        Runnable r1 = new Runnable() {
            String name = "Classe anonyme";
            @Override
            public void run() {
                System.out.println(this.name); // "Classe anonyme"
            }
        };

        Runnable r2 = () -> System.out.println(this.name); // "Classe externe"

        r1.run();
        r2.run();
    }
}

5. Erreurs typiques lors de l’utilisation des expressions lambda

Erreur n° 1: utilisation d’une variable non final/non effectivement final. Si vous décidez de modifier une variable après l’avoir utilisée dans une lambda, le compilateur vous rappellera immédiatement à l’ordre. C’est pour des raisons de sécurité : sinon, on ne saurait pas clairement quelle valeur de la variable utiliser.

Erreur n° 2: confusion avec this. Dans une expression lambda, this désigne la classe externe, tandis que dans une classe anonyme — la classe anonyme elle-même. Si vous essayez d’appeler une méthode de la classe externe depuis une lambda, tout fonctionnera, alors que depuis une classe anonyme — non (si vous comptiez sur le contexte de la classe externe).

Erreur n° 3: lambda sans contexte. Une expression lambda ne peut pas être utilisée seule — il faut soit l’affecter à une variable d’interface fonctionnelle, soit la passer là où une telle interface est attendue. Tenter d’écrire simplement x -> x + 1 hors contexte provoquera une erreur.

Erreur n° 4: lambda trop complexe. Si une expression lambda dépasse 3–5 lignes, elle devient difficile à lire. Dans ce cas, mieux vaut extraire la logique dans une méthode séparée.

1
Mission
JAVA 25 SELF, niveau 48, leçon 0
Bloqué
Atelier de tri des noms 📜
Atelier de tri des noms 📜
1
Mission
JAVA 25 SELF, niveau 48, leçon 0
Bloqué
Classificateur mystique des nombres 🔮
Classificateur mystique des nombres 🔮
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION