Objets immuables en Java

1. Vue d'ensemble

Dans ce didacticiel, nous allons apprendre ce qui rend un objet immuable, comment obtenir l'immuabilité en Java et quels sont les avantages qui en découlent.

2. Qu'est-ce qu'un objet immuable?

Un objet immuable est un objet dont l'état interne reste constant après avoir été entièrement créé .

Cela signifie que l'API publique d'un objet immuable nous garantit qu'il se comportera de la même manière pendant toute sa durée de vie.

Si nous jetons un coup d'œil à la classe String , nous pouvons voir que même lorsque son API semble nous fournir un comportement modifiable avec sa méthode replace , la String d' origine ne change pas:

String name = "baeldung"; String newName = name.replace("dung", "----"); assertEquals("baeldung", name); assertEquals("bael----", newName);

L'API nous donne des méthodes en lecture seule, elle ne doit jamais inclure de méthodes qui modifient l'état interne de l'objet.

3. Le mot-clé final en Java

Avant d'essayer d'atteindre l'immuabilité en Java, nous devrions parler du mot-clé final .

En Java, les variables sont mutables par défaut, ce qui signifie que nous pouvons changer la valeur qu'elles contiennent .

En utilisant le mot-clé final lors de la déclaration d'une variable, le compilateur Java ne nous laisse pas changer la valeur de cette variable. Au lieu de cela, il signalera une erreur de compilation:

final String name = "baeldung"; name = "bael...";

Notez que final nous interdit seulement de changer la référence que contient la variable, il ne nous protège pas de changer l'état interne de l'objet auquel il fait référence en utilisant son API publique:

final List strings = new ArrayList(); assertEquals(0, strings.size()); strings.add("baeldung"); assertEquals(0, strings.size());

Le deuxième assertEquals échouera car l'ajout d'un élément à la liste modifie sa taille, par conséquent, ce n'est pas un objet immuable.

4. Immuabilité en Java

Maintenant que nous savons comment éviter les modifications du contenu d'une variable, nous pouvons l'utiliser pour construire l'API d'objets immuables.

Construire l'API d'un objet immuable nous oblige à garantir que son état interne ne changera pas quelle que soit la façon dont nous utilisons son API.

Un pas en avant dans la bonne direction consiste à utiliser final lors de la déclaration de ses attributs:

class Money { private final double amount; private final Currency currency; // ... }

Notez que Java nous garantit que la valeur de montant ne changera pas, c'est le cas de toutes les variables de type primitif.

Cependant, dans notre exemple, nous sommes seulement garantis que la devise ne changera pas, nous devons donc nous fier à l' API Currency pour se protéger des changements .

La plupart du temps, nous avons besoin des attributs d'un objet pour contenir des valeurs personnalisées, et l'endroit où initialiser l'état interne d'un objet immuable est son constructeur:

class Money { // ... public Money(double amount, Currency currency) { this.amount = amount; this.currency = currency; } public Currency getCurrency() { return currency; } public double getAmount() { return amount; } }

Comme nous l'avons déjà dit, pour répondre aux exigences d'une API immuable, notre classe Money n'a que des méthodes en lecture seule.

En utilisant l'API de réflexion, nous pouvons briser l'immuabilité et modifier les objets immuables. Cependant, la réflexion viole l'API publique de l'objet immuable, et nous devons généralement éviter de le faire.

5. Avantages

Étant donné que l'état interne d'un objet immuable reste constant dans le temps, nous pouvons le partager en toute sécurité entre plusieurs threads .

Nous pouvons également l'utiliser librement, et aucun des objets qui le référencent ne remarquera aucune différence, nous pouvons dire que les objets immuables sont sans effets secondaires .

6. Conclusion

Les objets immuables ne changent pas leur état interne dans le temps, ils sont thread-safe et sans effets secondaires. En raison de ces propriétés, les objets immuables sont également particulièrement utiles lorsqu'il s'agit d'environnements multi-threads.

Vous pouvez trouver les exemples utilisés dans cet article sur GitHub.