Guide complet : Les record en Java¶
1. Introduction¶
Les record ont été introduits en Java 16 (en preview) et finalisés en Java 17. Ils permettent de déclarer des *
classes de données immutables* de manière concise, en réduisant considérablement la quantité de code boilerplate (
constructeurs, getters, equals, hashCode, toString). Un record est idéal pour modéliser des données simples,
comme des coordonnées, des points, des paires clé-valeur, etc.
2. Pourquoi utiliser un record ?¶
- Immutabilité : Les champs sont implicitement
final. - Concis : Pas besoin d’écrire manuellement les getters,
equals,hashCode, outoString. - Clarté : La déclaration est compacte et exprime clairement l’intention : stocker des données.
- Sécurité : Moins de risques d’erreurs dans les méthodes générées automatiquement.
3. Syntaxe de base¶
Un record se déclare ainsi :
public record NomDuRecord(Type1 champ1, Type2 champ2, ...) {
// Corps optionnel (constructeurs, méthodes, etc.)
}
- Java génère automatiquement :
- Un constructeur canonique (qui initialise tous les champs).
- Des méthodes d’accès (
champ1(),champ2(), etc., **pas deget**). equals(),hashCode(), ettoString().
4. Exemple : Conversion de Coord en record¶
a. Classe originale (avec boilerplate)¶
import java.util.Objects;
public class Coord {
private final int x;
private final int y;
public Coord(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Coord coord = (Coord) o;
return x == coord.x && y == coord.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
@Override
public String toString() {
return "(" + x + "," + y + ")";
}
}
b. Équivalent en record¶
- Utilisation :
5. Constructeurs personnalisés¶
Si vous voulez ajouter de la logique dans le constructeur (validation, calculs, etc.), vous pouvez définir un * constructeur compact* :
public record Coord(int x, int y) {
public Coord {
if (x < 0 || y < 0) {
throw new IllegalArgumentException("Les coordonnées doivent être positives.");
}
}
}
- Remarque : Le constructeur compact n’a pas de liste de paramètres explicite ; ils sont implicites (ceux du
record).
6. Méthodes supplémentaires¶
Vous pouvez ajouter des méthodes comme dans une classe normale :
public record Coord(int x, int y) {
public double distanceFromOrigin() {
return Math.sqrt(x * x + y * y);
}
}
- Utilisation :
7. Immutabilité et sécurité¶
- Les champs d’un
recordsontfinalet ne peuvent pas être modifiés après la construction. - Les
recordsont thread-safe par nature, car immutables. - Ils sont particulièrement utiles pour les DTO (Data Transfer Objects), les clés de maps, ou les valeurs dans des collections.
8. Comparaison avec une classe classique¶
| Fonctionnalité | Classe classique (ex: Coord) |
record (ex: Coord) |
|---|---|---|
| Déclaration | Verbose (champs, constructeur, etc.) | Concise (record Coord(int x, int y)) |
| Getters | getX(), getY() |
x(), y() |
equals, hashCode, toString |
Doivent être implémentés manuellement | Générés automatiquement |
| Immutabilité | Doit être gérée manuellement (final) |
Implicite |
| Constructeur personnalisé | Possible | Possible (constructeur compact) |
9. Exercices pratiques¶
Exercice 1 : Conversion en record¶
Convertissez la classe suivante en record :
public class Personne {
private final String nom;
private final int age;
public Personne(String nom, int age) {
this.nom = nom;
this.age = age;
}
public String getNom() {
return nom;
}
public int getAge() {
return age;
}
@Override
public boolean equals(Object o) { /* ... */ }
@Override
public int hashCode() { /* ... */ }
@Override
public String toString() { /* ... */ }
}
Solution attendue :
Exercice 2 : Ajout de logique¶
Créez un record Rectangle avec des champs largeur et hauteur (tous deux double), et ajoutez une méthode
aire() qui retourne la surface du rectangle.
Solution attendue :
public record Rectangle(double largeur, double hauteur) {
public double aire() {
return largeur * hauteur;
}
}
Exercice 3 : Validation dans le constructeur¶
Modifiez le record Coord pour que les coordonnées ne puissent pas être négatives (lancez une
IllegalArgumentException si c’est le cas).
Solution attendue :
public record Coord(int x, int y) {
public Coord {
if (x < 0 || y < 0) {
throw new IllegalArgumentException("Les coordonnées doivent être positives.");
}
}
}
Exercice 4 : Utilisation dans une collection¶
Écrivez un programme qui crée une List<Coord>, y ajoute quelques Coord, puis les affiche en utilisant toString()
généré automatiquement.
Solution attendue :
import java.util.List;
void main() {
List<Coord> coords = List.of(
new Coord(1, 2),
new Coord(3, 4),
new Coord(5, 6)
);
coords.forEach(System.out::println);
}
10. Points d’attention¶
- Pas d’héritage : Un
recordne peut pas hériter d’une autre classe (mais peut implémenter des interfaces). - Champs implicites : Tous les champs déclarés dans l’en-tête du
recordsont privés et finaux. - Constructeur par défaut : Si vous définissez un constructeur personnalisé, le constructeur canonique n’est plus généré automatiquement (sauf si vous utilisez le constructeur compact).
- Sérialisation : Les
recordsont sérialisables par défaut (implémententSerializable).
11. Pour aller plus loin¶
- Pattern Matching : Les
recordfonctionnent très bien avec le pattern matching (introduit en Java 16+). - Imbrication : Vous pouvez imbriquer des
recordou les utiliser comme champs d’autresrecord. - Performance : Les
recordsont optimisés par le compilateur pour être aussi performants que des classes équivalentes écrites manuellement.
12. Conclusion¶
Les record sont une avancée majeure pour Java, permettant de déclarer des classes de données de manière concise, sûre
et expressive. Ils réduisent le code boilerplate et rendent le code plus lisible et maintenable. En les utilisant, tes
étudiants pourront se concentrer sur la logique métier plutôt que sur les détails d’implémentation.