Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
💥 Les 22 & 23 novembre : Accès 100% GRATUIT
à la Bibliothèque Numérique ENI. Je m'inscris !
  1. Livres et vidéos
  2. Java Spring
  3. La programmation fonctionnelle
Extrait - Java Spring Construisez vos applications réactives avec une architecture micro-services en environnement Jakarta EE (2e édition)
Extraits du livre
Java Spring Construisez vos applications réactives avec une architecture micro-services en environnement Jakarta EE (2e édition) Revenir à la page d'achat du livre

La programmation fonctionnelle

Introduction

Les éléments fondamentaux de la programmation fonctionnelle avec les lambdas incluent les concepts suivants :

  • les expressions lambda,

  • les interfaces fonctionnelles,

  • les méthodes de référence,

  • les opérations sur les flux,

  • l’immutabilité,

  • les hauts ordres de fonctions,

  • la réduction de boucle.

Expressions lambda

Les expressions lambda sont des fonctions anonymes qui peuvent être utilisées comme des valeurs. Elles permettent de définir des blocs de code courts et concis pour effectuer des opérations sur des données sans avoir besoin de créer des classes séparées pour les fonctions.

Elles permettent de définir une fonction anonyme en utilisant une syntaxe concise. Par exemple, pour définir une fonction qui prend deux entiers en entrée et renvoie leur somme, il est possible d’utiliser une expression lambda :

// Définition de l'expression lambda  
BinaryOperator<Integer> sumFunction = (a, b) -> a + b;  
  
// Utilisation de l'expression lambda pour calculer 
// la somme de deux entiers  
int result = sumFunction.apply(10, 20);  
  
// Output: Résultat de la somme : 30  
System.out.println("Résultat de la somme : " + result); 

Interfaces fonctionnelles

Les interfaces fonctionnelles sont des interfaces qui ne contiennent qu’une seule méthode abstraite. Elles sont essentielles pour définir les types de paramètres et de retour des expressions lambda. Parmi les interfaces fonctionnelles intégrées en Java, on trouve Runnable, Callable, Comparator, Consumer, Predicate, etc.

Par exemple, l’interface Runnable est une interface fonctionnelle qui représente une tâche à exécuter de manière asynchrone :

// Définition de l'interface fonctionnelle Runnable  
Runnable  task = () -> {  
  for (int i = 0; i < 5; i++) {  
    System.out.println("Tâche en cours d'exécution...");  
  }  
};  
  
// Exécution de la tâche en utilisant un thread  
Thread thread = new Thread(task);  
thread.start();  
  
// Attendre que le thread se termine  
thread.join();  
} 

Méthodes de référence

Les méthodes de référence permettent de simplifier l’utilisation des lambdas en permettant de faire référence à des méthodes existantes par leur nom plutôt que d’écrire une expression lambda complète.

Par exemple, pour faire référence à une méthode statique, il est possible d’utiliser une méthode de référence :

// Définition d'une méthode statique  
public static void printMessage(String message) {  
    System.out.println("Message : " + message);  
}  
  
// Utilisation d'une méthode de référence pour faire référence à la 
// méthode statique  
Consumer<String> printer = ExampleClass::printMessage; 
 
// Affichage de : Message : Hello, world!  
printer.accept("Hello, world!"); 

Opérations sur les flux

Java 8 introduit les flux (streams) qui permettent de traiter des collections de données de manière fonctionnelle. Les flux offrent des opérations de transformation, de filtrage et de réduction, permettant d’effectuer des traitements sur les données de manière concise et expressive.

Par exemple, pour filtrer les nombres pairs d’une liste et les afficher :

  List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 
  
  numbers.stream()  
    .filter(n -> n % 2 == 0) // Filtrer les nombres pairs  
    .forEach(System.out::println); // Afficher les nombres pairs  
} 

Immutabilité

La programmation fonctionnelle favorise l’utilisation de données immuables, c’est-à-dire des données qui ne peuvent pas être modifiées une fois créées. Cela permet d’éviter les effets de bord et de rendre le code plus prévisible et sûr.

En Java, nous pouvons utiliser des classes immuables pour garantir que les objets ne peuvent pas être modifiés après leur création.

L’immutabilité s’annonce à l’aide du mot-clé final.

Par exemple, pour créer une classe immuable représentant une personne avec un nom et un âge :

final class Person {  
    private final String name;  
    private final int age;  
  
    public Person(String name, int age) {  
        this.name = name;  
        this.age = age;  
    }  
  
    // Méthodes d'accès aux propriétés (getters)  
    public String getName() {  
        return name;  
    }  
  
    public int getAge() {  
       ...

Haut ordre de fonction

En programmation fonctionnelle, les fonctions peuvent être traitées comme des objets de première classe, c’est-à-dire qu’elles peuvent être passées en tant que paramètres d’autres fonctions et retournées renvoyées en tant que résultats.

Par exemple, pour définir une fonction qui prend une liste d’entiers et applique une autre fonction à chaque élément :

  @Test  
  public void testSquareFunction() {  
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);  
  
    Function<Integer, Integer> squareFunction = n -> n * n;  
  
    List<Integer> squaredNumbers = numbers.stream()  
      .map(squareFunction)  
      .collect(Collectors.toList());  
  
    assertEquals(Arrays.asList(1, 4, 9, 16, 25), squaredNumbers);  
  }  
  

Réduction de boucle

Plutôt que d’utiliser des boucles traditionnelles, la programmation fonctionnelle encourage l’utilisation de fonctions de réduction (reduce) pour traiter les collections de données. Cela permet de réduire la complexité du code et d’améliorer la lisibilité.

Par exemple, pour calculer la somme de tous les éléments d’une liste d’entiers :

@Test  
public void testSum() {  
  List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);  
  
  int sum = numbers.stream()  
    .reduce(0, (a, b) -> a + b);  
  
  assertEquals(15, sum);  
} 

Conclusion

En combinant ces éléments fondamentaux, les développeurs Java peuvent tirer pleinement parti de la programmation fonctionnelle avec les expressions lambda pour écrire du code plus concis, maintenable, lisible et réactif tout en exploitant amplement les fonctionnalités de Java 8 et versions ultérieures.