Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Industrial programming >> Java

Maîtriser les expressions lambda Java : Des bases à l'API Stream

Maîtriser les expressions lambda Java : Des bases à l'API Stream

Java 8 a introduit les expressions lambda pour renforcer le pouvoir expressif du langage, permettant aux développeurs d'écrire du code plus concis et lisible.

Avant de plonger dans les lambdas, il est essentiel de comprendre les interfaces fonctionnelles, la colonne vertébrale de l'implémentation lambda.


Qu'est-ce qu'une interface fonctionnelle ?

Une interface fonctionnelle est une interface Java contenant exactement une méthode abstraite. Cette méthode unique définit le but de l'interface.

L'interface Runnable de java.lang est un exemple classique, exposant seulement la méthode run().

Exemple 1 : Définition d'une interface fonctionnelle

import java.lang.FunctionalInterface;

@FunctionalInterface
public interface MonInterface {
    // Unique méthode abstraite
    double getValue();
}

@FunctionalInterface force le compilateur à respecter le contrat d'interface fonctionnelle, empêchant l'ajout accidentel de multiples méthodes abstraites. L'annotation est optionnelle mais recommandée pour la clarté et la sécurité.

En Java 7, les interfaces fonctionnelles étaient appelées Single Abstract Methods (SAM) et implémentées via classes anonymes.

Exemple 2 : Implémentation d'un SAM avec classe anonyme

public class TestInterfaceFonctionnelle {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("J'ai implémenté l'interface fonctionnelle Runnable.");
            }
        }).start();
    }
}

Sortie :

J'ai implémenté l'interface fonctionnelle Runnable.

Les classes anonymes réduisent le code boilerplate, mais la syntaxe reste verbeuse. Les expressions lambda éliminent le besoin de noms de méthodes explicites lors du passage d'interfaces fonctionnelles comme arguments.


Introduction aux expressions lambda

Une expression lambda est essentiellement une méthode anonyme fournissant une implémentation concise de la méthode abstraite d'une interface fonctionnelle. Elle ne s'exécute pas indépendamment ; elle fournit le comportement pour la méthode de l'interface.

Comment définir une expression lambda en Java

La syntaxe générale est :

(liste paramètres) -> corps lambda

L'opérateur flèche (->) sépare la liste des paramètres du corps. Exemples concrets :

Supposons une méthode renvoyant Pi :

double getValeurPi() { return 3.1415; }

Avec une lambda :

() -> 3.1415

Sans paramètres, le côté gauche de la flèche est vide, le côté droit est le corps d'expression retournant la valeur.


Types de corps lambda

Les corps lambda Java se présentent sous deux formes :

1. Corps d'expression – contient une seule expression.

() -> System.out.println("Les lambdas sont géniales");

2. Corps de bloc – encapsulé dans des accolades, permettant plusieurs instructions et un return optionnel.

() -> { double pi = 3.1415; return pi; };

Note : Les corps d'expression n'exigent pas de return explicite, alors que les corps de bloc en nécessitent un si une valeur est retournée.


Exemple 3 : Expression lambda en pratique

Programme Java retournant Pi via une expression lambda.

import java.lang.FunctionalInterface;

@FunctionalInterface
interface MonInterface {
    double getValeurPi();
}

public class Main {
    public static void main(String[] args) {
        MonInterface ref = () -> 3.1415;
        System.out.println("Valeur de Pi = " + ref.getValeurPi());
    }
}

Sortie :

Valeur de Pi = 3.1415

Points clés :


Expressions lambda avec paramètres

Comme les méthodes, les lambdas peuvent accepter des paramètres. Exemple :

(n) -> (n % 2) == 0

Cette lambda vérifie si l'entier d'entrée est pair.

Exemple 4 : Inversion d'une chaîne avec lambda paramétrée

@FunctionalInterface
interface MonInterface {
    String inverse(String s);
}

public class Main {
    public static void main(String[] args) {
        MonInterface ref = (str) -> {
            String resultat = "";
            for (int i = str.length() - 1; i >= 0; i--) {
                resultat += str.charAt(i);
            }
            return resultat;
        };
        System.out.println("Lambda inversée = " + ref.inverse("Lambda"));
    }
}

Sortie :

Lambda inversée = adbmaL

Interface fonctionnelle générique

Les interfaces fonctionnelles peuvent être génériques, opérant sur tout type de donnée :

@FunctionalInterface
interface MonInterface<T> {
    T func(T t);
}

Exemple 5 : Lambdas génériques pour chaînes et entiers

public class Main {
    public static void main(String[] args) {
        // Inversion de chaîne
        MonInterface<String> inverse = (str) -> {
            String resultat = "";
            for (int i = str.length() - 1; i >= 0; i--) {
                resultat += str.charAt(i);
            }
            return resultat;
        };
        System.out.println("Lambda inversée = " + inverse.func("Lambda"));

        // Factorielle d'entier
        MonInterface<Integer> factorielle = (n) -> {
            int resultat = 1;
            for (int i = 1; i <= n; i++) {
                resultat *= i;
            }
            return resultat;
        };
        System.out.println("Factorielle de 5 = " + factorielle.func(5));
    }
}

Sortie :

Lambda inversée = adbmaL
Factorielle de 5 = 120

Expressions lambda et API Stream

Le package java.util.stream, introduit dans JDK 8, permet d'effectuer des opérations de style fonctionnel sur les collections : filtrage, transformation, réduction, tri, etc.

Considérons une liste de noms de lieux, chacun préfixé par son pays. Avec les streams et lambdas, on peut extraire et manipuler les données efficacement.

Exemple 6 : Traitement Stream avec lambdas

import java.util.ArrayList;
import java.util.List;

public class StreamMain {
    static List<String> lieux = new ArrayList<>();

    public static List<String> getLieux() {
        lieux.add("Népal, Katmandou");
        lieux.add("Népal, Pokhara");
        lieux.add("Inde, Delhi");
        lieux.add("USA, New York");
        lieux.add("Afrique, Nigeria");
        return lieux;
    }

    public static void main(String[] args) {
        List<String> mesLieux = getLieux();
        System.out.println("Lieux du Népal :");
        mesLieux.stream()
            .filter(p -> p.startsWith("Népal"))
            .map(p -> p.toUpperCase())
            .sorted()
            .forEach(p -> System.out.println(p));
    }
}

Sortie :

Lieux du Népal :
NÉPAL, KATMANDOU
NÉPAL, POKHARA

La chaîne d'opérations stream — filter, map, sorted, forEach — chacune acceptant une lambda, réduit drastiquement le boilerplate comparé aux solutions pré-Java-8.

Java

  1. Maîtriser les interfaces C# : Définition, implémentation et cas d'utilisation pratiques
  2. Maîtriser les opérateurs Java : Types, syntaxe et exemples pratiques
  3. Comprendre les expressions, instructions et blocs en Java
  4. Maîtriser les interfaces Java : concepts, implémentation et bonnes pratiques
  5. Maîtriser le try-with-resources Java : gestion automatique des ressources expliquée
  6. Annotations Java expliquées : types, placement et exemples pratiques
  7. Interface vs Classe Abstraite en Java : Comment Choisir la Bonne Abstraction
  8. Expressions régulières Java : Maîtriser la correspondance de motifs avec java.util.regex
  9. Comprendre les interfaces Java : Définitions, Usages et Bonnes pratiques