Les interfaces fonctionnelles en Java : explications et exemples

Concept : cette interface ne requiert aucun argument en entrée, mais produit une valeur de type T. Elle agit comme un fournisseur d'objets à la demande.

import java.time.LocalDate;
import java.util.function.Supplier;
import java.util.UUID;

// Exemple 1 : fournir une chaîne de caractères
Supplier<String> idGenerateur = () -> UUID.randomUUID().toString();
System.out.println(idGenerateur.get()); // Génère un identifiant unique

// Exemple 2 : fournir la date du jour
Supplier<LocalDate> dateActuelle = () -> LocalDate.now();
System.out.println(dateActuelle.get()); // Affiche la date courante

// Exemple 3 : création différée d'objets coûteux
Supplier<ConnexionBaseDeDonnees> connexionSupplier = () -> 
    new ConnexionBaseDeDonnees("jdbc:mysql://localhost:3306/mabase");

// L'objet n'est instancié que lors de l'appel à get()
// ConnexionBaseDeDonnees connexion = connexionSupplier.get();

Cnosumer<T>

Concept : cette interface accepte un paramètre de type T en entrée, mais ne retourne rien. Elle consomme ou traite les données passées.

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

// Exemple 1 : traitement simple
Consumer<String> afficheur = s -> System.out.println("Message : " + s);
afficheur.accept("Bonjour"); // Sortie : Message : Bonjour

// Exemple 2 : modification d'un objet
Consumer<Compte> activateur = compte -> {
    compte.setStatut("ACTIF");
    compte.setDerniereMiseAJour(LocalDate.now());
};

// Exemple 3 : itération sur une collection
List<Integer> nombres = Arrays.asList(10, 20, 30);
Consumer<Integer> calculateur = n -> {
    int carre = n * n;
    System.out.println("Carré de " + n + " : " + carre);
};
nombres.forEach(calculateur);
// Sortie :
// Carré de 10 : 100
// Carré de 20 : 400
// Carré de 30 : 900

Function<T,R>

Concept : cette interface prend un arguement de type T et renvoie un résultat de type R. Elle sert à transformer des données d'un type vers un autre.

import java.util.List;
import java.util.stream.Collectors;
import java.util.function.Function;

// Exemple 1 : conversion de type
Function<String, Integer> longueurChaine = s -> s.length();
int longueur = longueurChaine.apply("Interface"); // Retourne 9

// Exemple 2 : transformation d'objet
Function<Utilisateur, ProfilUtilisateur> versProfil = utilisateur -> {
    ProfilUtilisateur profil = new ProfilUtilisateur();
    profil.setNomComplet(utilisateur.getPrenom() + " " + utilisateur.getNom());
    profil.setEmail(utilisateur.getAdresseEmail());
    return profil;
};

// Exemple 3 : utilisation dans un Stream
List<String> produits = Arrays.asList("Ordinateur", "Téléphone", "Tablette");
List<Integer> longueurs = produits.stream()
    .map(longueurChaine)
    .collect(Collectors.toList()); // Résultat : [10, 9, 7]

Predicate<T>

Concept : cette interface évalue une condition sur un argument de type T et retourne un booléen. Elle est utilisée pour la validation ou le filtrage.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.function.Predicate;

// Exemple 1 : vérification numérique
Predicate<Integer> estPositif = n -> n > 0;
boolean test = estPositif.test(5); // Retourne true

// Exemple 2 : validation de chaîne
Predicate<String> contientEspace = s -> s != null && s.contains(" ");
System.out.println(contientEspace.test("Hello World")); // true

// Exemple 3 : composition de prédicats
Predicate<Employe> salaireEleve = e -> e.getSalaire() > 50000;
Predicate<Employe> experienceLongue = e -> e.getAnneesExperience() >= 5;
Predicate<Employe> candidatIdeal = salaireEleve.and(experienceLongue);

// Exemple 4 : filtrage dans un Stream
List<Double> mesures = Arrays.asList(12.5, -3.7, 0.0, 45.2);
List<Double> valeursValides = mesures.stream()
    .filter(v -> v > 0) // Utilisation directe d'un prédicat
    .collect(Collectors.toList()); // Résultat : [12.5, 45.2]

Utilisation combinée

// Traitement de données d'employés
List<Employe> equipe = Arrays.asList(
    new Employe("Alice", 28, 55000),
    new Employe("Bob", 35, 75000),
    new Employe("Charlie", 42, 48000)
);

// Définition des interfaces fonctionnelles
Predicate<Employe> senior = e -> e.getAge() > 30;
Function<Employe, String> extraireNom = Employe::getNom;
Consumer<String> journal = nom -> System.out.println("Employé senior : " + nom);

// Application en pipeline
equipe.stream()
    .filter(senior)
    .map(extraireNom)
    .forEach(journal);
// Sortie :
// Employé senior : Bob
// Employé senior : Charlie

// Application à un système de cache
public class GestionnaireCache {
    private CacheDistribue cache;

    public Object obtenirAvecFallback(String cle, Supplier<Object> sourceDonnees) {
        Object valeurCache = cache.recuperer(cle);
        if (valeurCache == null) {
            valeurCache = sourceDonnees.get(); // Appel différé
            cache.stocker(cle, valeurCache);
        }
        return valeurCache;
    }
}

Comparaison des interfaces

Interface Paramètre Retour Utilsiation typique
Supplier<T> Aucun T Fourniture de données, instanciation différée
Consumer<T> T Aucun Traitement, effets de bord, journalisation
Function<T,R> T R Conversion, mappage, calculs
Predicate<T> T boolean Filtrage, validation, conditions

Étiquettes: Java interfaces fonctionnelles Supplier Consumer Function

Publié le 9 juillet à 18h55