Programmation fonctionnelle en Java : de A à Z
La programmation fonctionnelle (PF) est un paradigme dans lequel les calculs sont considérés comme l'évaluation de fonctions mathématiques, en évitant les changements d'état et les données mutables. Java, traditionnellement un langage impératif, intègre activement des éléments de PF depuis la version 8. Cela permet d'écrire un code plus concis, prévisible et facilement testable, en particulier lors du travail avec des collections et des opérations asynchrones.
Dans cet article, nous allons détailler les concepts clés de la PF en Java : des expressions lambda aux techniques avancées de travail avec le Stream API et Option. Vous apprendrez comment appliquer ces outils en pratique pour que votre code devienne plus propre et plus efficace.
1. Bases : Expressions lambda et Interfaces fonctionnelles
L'expression lambda est une forme concise d'écriture d'une méthode anonyme (fonction). Elle permet de passer un comportement comme donnée. Syntaxe : (paramètres) -> corps.
L'interface fonctionnelle est une interface avec une seule méthode abstraite (SAM — Single Abstract Method). Ce sont précisément ces interfaces qui peuvent être implémentées à l'aide de lambdas. Les interfaces intégrées clés dans java.util.function :
Predicate<T>— prend un objet T, retourne un boolean (méthodetest).Function<T, R>— prend T, retourne R (méthodeapply).Consumer<T>— prend T, ne retourne rien (méthodeaccept).Supplier<T>— ne prend rien, retourne T (méthodeget).UnaryOperator<T>— cas particulier de Function, où T et R sont du même type.
Exemple : Tri d'une liste avec Comparator (interface fonctionnelle)
import java.util.*;
public class LambdaExample { public static void main(String[] args) { List<String> names = Arrays.asList("Иван", "Анна", "Петр", "Мария");
// Sans lambda (classe anonyme) Collections.sort(names, new Comparator<String>() { @Override public int compare(String a, String b) { return a.length() - b.length(); } });
// Avec lambda Collections.sort(names, (a, b) -> a.length() - b.length());
// Encore plus court : utiliser Comparator.comparingInt names.sort(Comparator.comparingInt(String::length));
System.out.println(names); // [Анна, Петр, Иван, Мария] }}Exemple : Utilisation de Predicate pour le filtrage
import java.util.function.Predicate;
public class PredicateExample { public static void main(String[] args) { Predicate<Integer> isEven = n -> n % 2 == 0; Predicate<Integer> isPositive = n -> n > 0;
System.out.println(isEven.test(4)); // true System.out.println(isEven.and(isPositive).test(-2)); // false (pas positif) System.out.println(isEven.or(isPositive).test(-3)); // true (pas pair, mais positif ? non, -3 n'est pas positif) }}2. Stream API : Un outil puissant de traitement de données
Le Stream API est une séquence d'éléments supportant des opérations parallèles et séquentielles. Un Stream ne stocke pas les données, mais les transforme. Les opérations clés sont divisées en opérations intermédiaires (retournent un nouveau Stream) et terminales (terminent le traitement et retournent un résultat ou un effet secondaire).
Opérations principales du Stream API :
- Intermédiaires :
filter,map,flatMap,sorted,distinct,peek,limit,skip. - Terminales :
collec