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