Java 8 est sans doute la version la plus importante de l'histoire du langage depuis Java 5. Sorti en mars 2014, il a introduit la programmation fonctionnelle dans Java, transformant radicalement la façon d'écrire du code. Dix ans plus tard, ses fonctionnalités restent au cœur du développement Java quotidien.

1. Expressions Lambda

Les expressions lambda permettent de traiter les fonctions comme des valeurs de première classe. Elles éliminent le boilerplate des classes anonymes.

Java — Avant / Après Java 8
// ❌ Avant Java 8 — classe anonyme verbeux
Comparator<String> comp = new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
};

// ✅ Java 8 — lambda concis
Comparator<String> comp = (a, b) -> a.compareTo(b);

// Utilisation dans un tri
List<String> noms = Arrays.asList("Charlie", "Alice", "Bob");
noms.sort((a, b) -> a.compareTo(b));

// Runnable sans argument
Runnable r = () -> System.out.println("Hello lambda!");

2. Stream API

L'API Stream est la killer feature de Java 8. Elle permet de traiter des collections de façon déclarative, en enchaînant des opérations fonctionnelles.

Java
List<String> developpeurs = List.of(
    "Alice", "Bob", "Charlie", "David", "Eve"
);

// Filtrer, transformer, collecter
List<String> resultat = developpeurs.stream()
    .filter(n -> n.length() > 3)        // filtrage
    .map(String::toUpperCase)             // transformation
    .sorted()                              // tri
    .collect(Collectors.toList());        // collecte

// Agrégation
OptionalInt max = IntStream.of(3, 1, 4, 1, 5, 9)
    .filter(n -> n % 2 == 0)
    .max();

// Groupement
Map<Integer, List<String>> parLongueur = developpeurs.stream()
    .collect(Collectors.groupingBy(String::length));

// Traitement parallèle — gratuit !
long count = developpeurs.parallelStream()
    .filter(n -> n.startsWith("A"))
    .count();

3. Interfaces fonctionnelles & java.util.function

Java 8 introduit le package java.util.function avec des interfaces fonctionnelles prêtes à l'emploi.

Java
// Function<T, R> — transforme T en R
Function<String, Integer> longueur = String::length;
Function<String, String> upper = String::toUpperCase;
Function<String, String> compose = longueur.andThen(i -> i + " chars");

// Predicate<T> — teste une condition
Predicate<String> estVide  = String::isEmpty;
Predicate<String> nonVide  = estVide.negate();
Predicate<String> longNonVide = nonVide.and(s -> s.length() > 5);

// Consumer<T> — consomme sans retour
Consumer<String> print = System.out::println;

// Supplier<T> — produit une valeur
Supplier<List<String>> newList = ArrayList::new;

// BiFunction<T, U, R>
BiFunction<String, String, String> concat = (a, b) -> a + b;

4. Optional — Fin des NullPointerException

Optional<T> est un conteneur qui représente explicitement la présence ou l'absence d'une valeur, forçant le développeur à gérer les cas null.

Java
// Création
Optional<String> present = Optional.of("valeur");
Optional<String> vide    = Optional.empty();
Optional<String> nullable = Optional.ofNullable(getPossiblyNull());

// Utilisation en chaîne
String resultat = rechercherUtilisateur("alice")
    .map(User::getNom)
    .filter(n -> n.length() > 2)
    .orElse("Inconnu");

// orElseGet — paresseux (lazy)
String nom = optional.orElseGet(() -> genererNomDefaut());

// orElseThrow
User user = optional.orElseThrow(() ->
    new EntityNotFoundException("Utilisateur introuvable"));

// ifPresent
optional.ifPresent(v -> System.out.println("Trouvé : " + v));

5. API Date / Time — java.time

L'ancienne API (java.util.Date, Calendar) était notoire pour ses bugs et son manque d'intuitivité. Java 8 introduit java.time, inspirée de Joda-Time.

Java
// Date seule (sans heure)
LocalDate today = LocalDate.now();
LocalDate noel  = LocalDate.of(2025, Month.DECEMBER, 25);
long jours = ChronoUnit.DAYS.between(today, noel);

// Date + Heure
LocalDateTime maintenant = LocalDateTime.now();
LocalDateTime demain = maintenant.plusDays(1).withHour(9);

// Avec fuseau horaire
ZonedDateTime paris = ZonedDateTime.now(ZoneId.of("Europe/Paris"));

// Durée
Duration d = Duration.ofHours(2).plusMinutes(30);

// Formatage
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
String formatted = maintenant.format(fmt);

6. Méthodes default dans les interfaces

Java 8 permet d'ajouter des méthodes avec implémentation dans les interfaces via le mot-clé default. Cela permet d'enrichir les interfaces existantes sans casser la compatibilité.

Java
public interface Service {
    void execute();  // méthode abstraite

    default void executeWithLog() {
        System.out.println("Début : " + LocalDateTime.now());
        execute();
        System.out.println("Fin : " + LocalDateTime.now());
    }

    static Service noOp() {
        return () -> {}; // lambda qui implémente execute()
    }
}

7. Références de méthodes

Les références de méthodes (::) sont du sucre syntaxique pour les lambdas qui appellent une méthode existante.

Java
// Méthode statique
Function<String, Integer> parse = Integer::parseInt;

// Méthode d'instance sur objet spécifique
String prefix = "Hello";
Predicate<String> starts = prefix::startsWith;

// Méthode d'instance sur type arbitraire
Function<String, String> upper = String::toUpperCase;

// Constructeur
Supplier<ArrayList> newList = ArrayList::new;

// Usage pratique
noms.stream()
    .map(String::trim)
    .filter(String::isBlank)
    .forEach(System.out::println);

8. Récapitulatif Java 8

λ

Lambdas

Fonctions anonymes concises, fin des classes anonymes verbeux.

🌊

Stream API

Traitement déclaratif des collections : filter, map, reduce, collect.

📦

Optional<T>

Gestion explicite et sécurisée des valeurs potentiellement nulles.

📅

java.time API

LocalDate, LocalDateTime, ZonedDateTime — gestion des dates modernisée.

🔗

Méthodes default

Implémentations par défaut dans les interfaces, rétrocompatibilité.

Références de méthodes

Classe::méthode — lambdas encore plus concis et lisibles.

Java 8 a atteint sa fin de support Oracle gratuit en janvier 2019, mais reste largement utilisé en entreprise. La plupart des frameworks (Spring Boot, Hibernate) ont migré vers Java 17+ comme minimum recommandé.