Le langage Java propose de plus en plus de mécanismes et constructions du langage tendant à **empêcher de modifier ou de muter des données**. Cela a des avantages lorsqu'on fait de la [[Glossaire#Programmation concurrente|programmation concurrente]]. Car les objets immuable sont considérés [[Glossaire#Thread-safe|thread-safe]] ## Différence entre immuabilité et non-modifiable - **Immuable** (immutable en anglais) : la valeur ainsi que l'ensemble des valeurs qu'il contient ne peut pas être modifié **ET** l'ensemble du graphe de ses valeurs et références qu'il contient ne peuvent pas être modifiées. Certains parlent **d'immuabilité profonde**. - **Non-modifiable** : la valeur ne peut pas être modifié, les valeurs qu'il contient ne peuvent pas être modifiés. Mais les valeurs pointées par les références qu'il contient peuvent être modifiées. Certains parlent **d'immuabilité de surface**. Ex. Une variable `final` n'empêche pas de modifier les données à l'intérieur de cette variaable. ```java List<String> lettres = new ArrayList<String>(); lettres.add("A"); lettres.add("B"); lettres.add("C"); ``` ## Exemple plus complet ```java import java.util.ArrayList; import java.util.List; class MutableVsUnmodifiableVsImmutable { static void main() { unmodifiable(); immutable(); mutable(); } private static void immutable() { System.out.println("Immutable data in non-modifiable collection"); record ImmutablePoint(int x, int y) { @Override public String toString() { return "{" + "x=" + x + ", y=" + y + '}'; } } ImmutablePoint a = new ImmutablePoint(1, 2); List<ImmutablePoint> points = List.of(a); System.out.println(points); // [{x=1, y=2}] // Erreur de compilation : "Cannot assign a value to final variable 'x'" // a.x=5; a = new ImmutablePoint(5, 6); // System.out.println(points); // [{x=1, y=2}] } private static void unmodifiable() { System.out.println("Mutable data in non-modifiable collection"); MutablePoint a = new MutablePoint(1, 2); List<MutablePoint> points = List.of(a); System.out.println(points); // [{x=1, y=2}] a.x = 5; a.y = 6; System.out.println(points); // [{x=5, y=6}] a = new MutablePoint(7, 8); System.out.println(points); // [{x=5, y=6}] // java.lang.UnsupportedOperationException //points.add(a); } private static void mutable() { System.out.println("Mutable data in mutable collection"); MutablePoint a = new MutablePoint(1, 2); // ArrayList est une collection modifiable List<MutablePoint> points = new ArrayList<>(List.of(a)); System.out.println(points); // [{x=1, y=2}] a.x = 5; a.y = 6; System.out.println(points); // [{x=5, y=6}] a = new MutablePoint(7, 8); // Modifier la référence de a hors de la collection ne change pas sa valeur à l'intérieur de celle-ci // Car la référence a été copiée lors du passage de paramètre System.out.println(points); // [{x=5, y=6}] // Ajouter un élément à la liste est une mutation points.add(a); System.out.println(points); // [{x=5, y=6}] } static class MutablePoint { public int x; public int y; public MutablePoint(int x, int y) { this.x = x; this.y = y; } @Override public String toString() { return "{" + "x=" + x + ", y=" + y + '}'; } } } ``` ## Exo muable ou immuable ? ### Exo 1 - Point ```java record Point(Integer x, Integer y) { } Integer x=1; Integer y=2; Point point = new Point(x, y); System.out.println(point); // 1️⃣ Point[x=?, y=?] x = 2; System.out.println(point); // 2️⃣ Point[x=?, y=?] ``` - 1️⃣ : `Point[x = ? , y = ?]` - 2️⃣ : `Point[x = ? , y = ?]` - Est-ce que la variable `Point point` est modifiable ? - Est-ce que le record `Point` est modifiable ? - Est-ce que le record `Point` est immuable ? ### Exo 2 - Personne ```java class Adresse { public String line1; public String line2; public Adresse(String line1, String line2) { this.line1 = line1; this.line2 = line2; } @Override public String toString() { return "Adresse{" + "line1='" + line1 + '\'' + ", line2='" + line2 + '\'' + '}'; } } record Personne(Adresse adress, String name) { } Adresse adresse = new Adresse("LIGNE 1", "LIGNE 2"); final Personne janeDoe = new Personne(adresse, "Jane Doe"); System.out.println(janeDoe); // 1️⃣ Personne[adress=Adresse{line1=?, line2=?}, name=?] adresse.line1 = "THE MOON"; System.out.println(janeDoe); // 2 Personne[adress=Adresse{line1=?, line2=?}, name=?] ``` - 1️⃣ : `Personne[adress=Adresse{line1=?, line2=?}, name=?]` - 2️⃣ : `Personne[adress=Adresse{line1=?, line2=?}, name=?]` - Est-ce que la variable `Personne janeDoe` est modifiable ? - Est-ce que le record `Personne` est modifiable ? - Est-ce que le record `Personne` est immuable ? ## Constructions immuables ### String Le chaines de caractères peuvent être considérées immuable en Java. ```java class Testimmutablestring{ public static void main(String args[]){ String s="Future"; s.concat(" World");//concat() method appends the string at the end System.out.println(s);//will print Future because strings are immutable objects } } ``` ### Record En Java, un record est une structure de données qui - garantit que ses **attributs directs** sont **privés** ne peuvent pas être réaffectés (`final`) - accesseurs pour chaques attributs : `nomAttribut()` - pas de setters possibles - ne peut **pas être étendue ou héritée** ( `final`) - implémente sont **identité** (`equals()`, `hashcode()`) par la valeur de ses attributs - implémente `toString` via l'ensemble des valeurs de ses attributs - expose un constructeur par défaut pour l'ensemble de ses attributs Leur syntaxe est particulièrement compacte et **peu verbeuse** et en fait de bons candidats pour plusieurs cas d'usages comme par exemple : - renvoyer plusieurs valeurs d'une méthode - remplacer certaines structures de données utilisées par des frameworks : tuples, paires, ... - utiliser comme DTO ```java record Personne(Adresse adress, String name) { } ``` Est équivalent à (un [[Glossaire#Sucre syntaxique|sucre syntaxique]] pour) : ```java final class Personne { private final Adresse adress; private final String name; Personne(Adresse adress, String name) { this.adress = adress; this.name = name; } public Adresse adress() { return adress; } public String name() { return name; } @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || obj.getClass() != this.getClass()) return false; var that = (Personne) obj; return Objects.equals(this.adress, that.adress) && Objects.equals(this.name, that.name); } @Override public int hashCode() { return Objects.hash(adress, name); } @Override public String toString() { return "Personne[" + "adress=" + adress + ", " + "name=" + name + ']'; } } ``` ### API Java Time #todo ### BigDecimal / BigInteger #todo ### Constructions non-modifiables > Ex. > - Types immuable : `String`, `BigDecimal`, `BigInteger`, types de l'API `java.time` > - Collections non modifiables : `List.of(...)`, `Set.of(...)`, `Map.of(...)`, `Collections.unmodifiableXxxx(...) > - Types énumérés > - Records