Aller au contenu principal
Guide de Migration Java 8 → Java 20
Retour aux articles

Guide de Migration Java 8 → Java 20

RivoLink 1 min de lecture

Guide de Migration Java 8 → Java 20

Depuis la sortie de Java 8 en 2014, le langage Java a considérablement évolué. Java 20, publié en 2023, marque une étape importante avec des fonctionnalités mature et d'autres en mode "preview". Ce guide vous accompagne dans la compréhension des principales évolutions, du plus simple au plus avancé.

Contexte : Java 8, un point de départ majeur

Java 8 (2014) a révolutionné le langage avec :

  • Lambda expressions — Fonctions anonymes pour le style fonctionnel
  • Stream API — Traitement fonctionnel des collections
  • Optional — Gestion explicite des valeurs nulles
  • Default methods — Méthodes par défaut dans les interfaces
  • Date API (java.time) — Remplacement de Date/Calendar

Java 20 (2023) construit sur cette base avec des fonctionnalités plus subtiles mais puissantes.


1. Records (Java 14, standard en Java 16)

Les records simplifient la création de classes immuables conteneurs de données.

Java 8 (sans records)

java
public class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
    
    @Override
    public boolean equals(Object o) { ... }
    @Override
    public int hashCode() { ... }
    @Override
    public String toString() { ... }
}

Java 20

java
public record Person(String name, int age) {}

Avantages :

  • Code réduit de ~30 lignes à 1 ligne
  • Immutabilité garantie
  • Equals/hashCode/toString générés automatiquement
  • Support des records dans les switch (pattern matching)

2. Pattern Matching for switch (Java 21, preview en Java 20)

Les expressions switch sont maintenant plus puissantes avec le pattern matching.

Java 8

java
Object obj = ...;
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.toLowerCase());
} else if (obj instanceof Integer) {
    System.out.println(obj * 2);
}

Java 20 (preview) / 21 (standard)

java
Object obj = ...;
switch (obj) {
    case String s -> System.out.println(s.toLowerCase());
    case Integer i -> System.out.println(i * 2);
    case null -> System.out.println("Null value");
    default -> System.out.println("Unknown");
}

Records avec pattern matching

java
record Point(int x, int y) {}

void process(Object o) {
    switch (o) {
        case Point(int x, int y) -> System.out.println("Point: " + x + "," + y);
        case null -> System.out.println("Nothing");
        default -> System.out.println("Other");
    }
}

3. Text Blocks (Java 15, standard en Java 16)

Les text blocks simplifient l'écriture de chaînes multilignes.

Java 8

java
String json = "{\n" +
    "  \"name\": \"John\",\n" +
    "  \"age\": 30\n" +
    "}";

Java 20

java
String json = """
{
  "name": "John",
  "age": 30
}
""";

Avantages :

  • Pas besoin d'échapper les quotes
  • Meilleure lisibilité
  • Indentation naturelle

4. Optional Améliorations (Java 9+)

Java 8 a introduit Optional, mais Java 9+ l'a enrichi.

Java 8

java
Optional<String> optional = Optional.ofNullable(getValue());
if (optional.isPresent()) {
    System.out.println(optional.get().toUpperCase());
}

Java 20 (avec améliorations successives)

java
Optional<String> optional = Optional.ofNullable(getValue());

// Optional.or() - Java 9
Optional<String> result = optional.or(() -> Optional.of("default"));

// Optional.stream() - Java 9
optional.stream().forEach(System.out::println);

// Optional.isEmpty() - Java 11
if (optional.isEmpty()) {
    System.out.println("No value");
}

5. Virtual Threads (Project Loom, Java 21, preview en Java 20)

Les virtual threads révolutionnent la concurrence avec des threads légers.

Java 8 (platform threads)

java
// Chaque thread = 1 OS thread (coûteux)
ExecutorService executor = Executors.newFixedThreadPool(100);
executor.submit(() -> {
    // Traitement
});

Java 20/21 (virtual threads)

java
// Virtual threads - légers, millions peuvent coexister
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
executor.submit(() -> {
    // Traitement
});

Cas d'usage : Serveurs haute concurrence, microservices.


6. Switch Expressions (Java 14, standard en Java 12)

Les switch deviennent des expressions avec yield.

Java 8

java
int days;
switch (month) {
    case 1: case 3: case 5: case 7: case 8: case 10: case 12:
        days = 31;
        break;
    case 4: case 6: case 9: case 11:
        days = 30;
        break;
    case 2:
        days = 28;
        break;
    default:
        days = 0;
}

Java 20

java
int days = switch (month) {
    case 1, 3, 5, 7, 8, 10, 12 -> 31;
    case 4, 6, 9, 11 -> 30;
    case 2 -> 28;
    default -> 0;
};

Avec yield pour des blocs plus complexes :

java
int result = switch (value) {
    case 1 -> {
        yield 10;
    }
    case 2 -> {
        int x = value * 2;
        yield x + 5;
    }
    default -> 0;
};

7. Records Patterns (Java 21)

Les records s'intègrent parfaitement avec les patterns.

java
record Point(int x, int y) {}
record Rectangle(Point upperLeft, Point lowerRight) {}

void process(Rectangle rect) {
    // Déconstruction directe
    if (rect instanceof Rectangle(Point(int x1, int y1), Point(int x2, int y2))) {
        System.out.println("Rectangle from (" + x1 + "," + y1 + ") to (" + x2 + "," + y2 + ")");
    }
}

8. Strongly Encapsulate JDK Internals (Java 16+)

Par défaut, l'accès aux API internes (sun.misc.Unsafe, etc.) est restreint.

Message d'erreur typique

text
ERROR: Access denied for sun.misc.Unsafe

Solution

Soit on utilise l'API officielle, soit on ajoute des options :

bash
--add-opens java.base/java.util=ALL-UNNAMED

Meilleure pratique : Éviter les internal APIs, utiliser les alternatives officielles.


9. Type Inference for Local Variables (Java 10)

Le mot-clé var pour les variables locales.

Java 8

java
List<String> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();

Java 20

java
var list = new ArrayList<String>();
var map = new HashMap<String, Integer>();

Note : var n'est pas une keyword, juste un type réservé. Ne s'applique qu'aux variables locales.


10. Multi-File Source-Code Execution (Java 11+)

Lancer un fichier Java unique sans compilation préalable.

Java 8

bash
javac Main.java
java Main

Java 20

bash
java Main.java

Fonctionne même avec plusieurs fichiers dans le même répertoire.


11. Concise Method Bodies (Java 21, preview)

Syntaxe simplifiée pour les méthodes sans paramètres.

java
// Preview en Java 21
class DateUtils {
    int year() { 365; }  // Return implicite
    
    String season() {
        switch (month) {
            case 12, 1, 2 -> "Winter";
            case 3, 4, 5 -> "Spring";
            // ...
        }
    }
}

Migration : Checklist Pratique

Étape 1 : Mise à jour du JDK

bash
# Vérifier la version
java -version

# Installer Java 20/21
# Ubuntu/Debian : apt install openjdk-21-jdk
# macOS : brew install openjdk

Étape 2 : Mise à jour du build

Maven (pom.xml)

xml
<properties>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
</properties>

Gradle (build.gradle)

groovy
java {
    sourceCompatibility = JavaVersion.VERSION_21
    targetCompatibility = JavaVersion.VERSION_21
}

Étape 3 : Réfactoring progressif

  1. Remplacer les POJOs par des records
  2. Utiliser le pattern matching dans les switch
  3. Adopter les text blocks pour les chaînes multilignes
  4. Remplacer Optional.isEmpty() par isEmpty() (Java 11+)
  5. Utiliser var pour les variables locales

Étape 4 : Gestion des breaking changes

  • Joueur avec les --add-opens si nécessaire
  • Vérifier les dépendances tierces
  • Tests automatisés pour valider la migration

Conclusion

Java 20/21 n'est pas une révolution comme Java 8, mais une évolution continue qui rend le langage plus expressif et plus performant.

Points à retenir :

  • Records — Simplifient les classes de données
  • Pattern matching — Switch plus puissant avec déconstruction
  • Virtual threads — Concurrence à grande échelle
  • Text blocks — Chaînes multilignes naturelles
  • var — Inférence de type pour les locales

Pour une migration réussie, procédez progressivement, testez chaque nouvelle fonctionnalité et profitez des outils modernes de Java.


Annexe : Chronologie des fonctionnalités

VersionDateFonctionnalités majeures
82014Lambda, Stream, Optional, Date API
92017Modules, JShell, Optional.or/stream
102018var, G1 GC par défaut
112018LTS, var, isEmpty()
142020Records (preview), Switch expressions
152020Text blocks, Sealed classes
162021Records (standard), Pattern matching
172021LTS, Sealed classes, Pattern matching
192022Text blocks (standard), Virtual threads
202023Virtual threads (preview), Records patterns
212023Virtual threads (standard), Pattern matching (standard)