Introduction
Cet article donne un aperçu de la création d’une classe immuable en programmation Java.
Un objet est immuable lorsqu’il ne change pas d’état après son initialisation. Par exemple, String
est une classe immuable et, une fois instancié, la valeur d’un objet String
ne change jamais. Apprenez-en davantage sur pourquoi la classe String
est immuable en Java.
Étant donné qu’un objet immuable ne peut pas être mis à jour, les programmes doivent créer un nouvel objet pour chaque changement d’état. Cependant, les objets immuables présentent également les avantages suivants :
- Une classe immuable est bonne à des fins de mise en cache car vous n’avez pas à vous soucier des changements de valeur.
- Une classe immuable est intrinsèquement sûre pour le thread, donc vous n’avez pas à vous soucier de la sécurité des threads dans des environnements multi-thread.
Apprenez-en davantage sur le multi-threading en Java et parcourez les questions d’entretien sur le multi-threading en Java.
Création d’une classe immuable en Java
Pour créer une classe immuable en Java, suivez ces principes généraux:
- Déclarez la classe comme
final
pour qu’elle ne puisse pas être étendue. - Rendez tous les champs
private
afin que l’accès direct ne soit pas autorisé. - Ne fournissez pas de méthodes setter pour les variables.
- Rendez tous les champs mutables
final
afin qu’une valeur de champ ne puisse être assignée qu’une seule fois. - Initialisez tous les champs à l’aide d’une méthode constructeur effectuant une copie profonde.
- Effectuez une copie des objets dans les méthodes getter pour renvoyer une copie plutôt que la référence réelle de l’objet.
La classe suivante est un exemple qui illustre les bases de l’immuabilité. La classe FinalClassExample
définit les champs et fournit la méthode constructeur qui utilise une copie profonde pour initialiser l’objet. Le code dans la méthode main
du fichier FinalClassExample.java
teste l’immuabilité de l’objet.
Créez un nouveau fichier appelé FinalClassExample.java
et copiez le code suivant :
import java.util.HashMap;
import java.util.Iterator;
public final class FinalClassExample {
// champs de la classe FinalClassExample
private final int id;
private final String name;
private final HashMap<String,String> testMap;
public int getId() {
return id;
}
public String getName() {
return name;
}
// Fonction Getter pour les objets mutables
public HashMap<String, String> getTestMap() {
return (HashMap<String, String>) testMap.clone();
}
// Méthode du constructeur effectuant une copie profonde
public FinalClassExample(int i, String n, HashMap<String,String> hm){
System.out.println("Performing Deep Copy for Object initialization");
// Le mot-clé "this" fait référence à l'objet actuel
this.id=i;
this.name=n;
HashMap<String,String> tempMap=new HashMap<String,String>();
String key;
Iterator<String> it = hm.keySet().iterator();
while(it.hasNext()){
key=it.next();
tempMap.put(key, hm.get(key));
}
this.testMap=tempMap;
}
// Tester la classe immuable
public static void main(String[] args) {
HashMap<String, String> h1 = new HashMap<String,String>();
h1.put("1", "first");
h1.put("2", "second");
String s = "original";
int i=10;
FinalClassExample ce = new FinalClassExample(i,s,h1);
// imprimer les valeurs de ce
System.out.println("ce id: "+ce.getId());
System.out.println("ce name: "+ce.getName());
System.out.println("ce testMap: "+ce.getTestMap());
// changer les valeurs de la variable locale
i=20;
s="modified";
h1.put("3", "third");
// imprimer à nouveau les valeurs
System.out.println("ce id after local variable change: "+ce.getId());
System.out.println("ce name after local variable change: "+ce.getName());
System.out.println("ce testMap after local variable change: "+ce.getTestMap());
HashMap<String, String> hmTest = ce.getTestMap();
hmTest.put("4", "new");
System.out.println("ce testMap after changing variable from getter methods: "+ce.getTestMap());
}
}
Compiler et exécuter le programme :
- javac FinalClassExample.java
- java FinalClassExample
Remarque : Vous pourriez obtenir le message suivant lors de la compilation du fichier : Remarque : FinalClassExample.java utilise des opérations non vérifiées ou non sécurisées
car la méthode Getter utilise une conversion non vérifiée de HashMap<String,String>
en Object
. Vous pouvez ignorer l’avertissement du compilateur à des fins d’exemple.
Vous obtenez la sortie suivante :
OutputPerforming Deep Copy for Object initialization
ce id: 10
ce name: original
ce testMap: {1=first, 2=second}
ce id after local variable change: 10
ce name after local variable change: original
ce testMap after local variable change: {1=first, 2=second}
ce testMap after changing variable from getter methods: {1=first, 2=second}
La sortie montre que les valeurs de HashMap n’ont pas changé car le constructeur utilise une copie profonde et la fonction Getter renvoie un clone de l’objet d’origine.
Que se passe-t-il lorsque vous n’utilisez pas de copie profonde et de clonage
Vous pouvez apporter des modifications au fichier FinalClassExample.java
pour montrer ce qui se passe lorsque vous utilisez une copie superficielle au lieu d’une copie profonde et que vous retournez l’objet au lieu d’une copie. L’objet n’est plus immuable et peut être modifié. Effectuez les modifications suivantes dans le fichier exemple (ou copiez et collez à partir de l’exemple de code):
- Supprimez la méthode constructeur fournissant une copie profonde et ajoutez la méthode constructeur fournissant une copie superficielle qui est surlignée dans l’exemple suivant.
- Dans la fonction getter, supprimez
return (HashMap<String, String>) testMap.clone();
et ajoutezreturn testMap;
.
Le fichier exemple devrait maintenant ressembler à ceci:
import java.util.HashMap;
import java.util.Iterator;
public final class FinalClassExample {
// champs de la classe FinalClassExample
private final int id;
private final String name;
private final HashMap<String,String> testMap;
public int getId() {
return id;
}
public String getName() {
return name;
}
// Fonction getter pour les objets mutables
public HashMap<String, String> getTestMap() {
return testMap;
}
// Méthode constructeur effectuant une copie superficielle
public FinalClassExample(int i, String n, HashMap<String,String> hm){
System.out.println("Performing Shallow Copy for Object initialization");
this.id=i;
this.name=n;
this.testMap=hm;
}
// Tester la classe immuable
public static void main(String[] args) {
HashMap<String, String> h1 = new HashMap<String,String>();
h1.put("1", "first");
h1.put("2", "second");
String s = "original";
int i=10;
FinalClassExample ce = new FinalClassExample(i,s,h1);
// imprimer les valeurs de ce
System.out.println("ce id: "+ce.getId());
System.out.println("ce name: "+ce.getName());
System.out.println("ce testMap: "+ce.getTestMap());
// modifier les valeurs des variables locales
i=20;
s="modified";
h1.put("3", "third");
// imprimer à nouveau les valeurs
System.out.println("ce id after local variable change: "+ce.getId());
System.out.println("ce name after local variable change: "+ce.getName());
System.out.println("ce testMap after local variable change: "+ce.getTestMap());
HashMap<String, String> hmTest = ce.getTestMap();
hmTest.put("4", "new");
System.out.println("ce testMap after changing variable from getter methods: "+ce.getTestMap());
}
}
Compilez et exécutez le programme:
- javac FinalClassExample.java
- java FinalClassExample
Vous obtenez la sortie suivante :
OutputPerforming Shallow Copy for Object initialization
ce id: 10
ce name: original
ce testMap: {1=first, 2=second}
ce id after local variable change: 10
ce name after local variable change: original
ce testMap after local variable change: {1=first, 2=second, 3=third}
ce testMap after changing variable from getter methods: {1=first, 2=second, 3=third, 4=new}
La sortie montre que les valeurs de la HashMap ont été modifiées car la méthode du constructeur utilise une copie superficielle, il y a une référence directe à l’objet d’origine dans la fonction getter.
Conclusion
Vous avez appris certains des principes généraux à suivre lorsque vous créez des classes immuables en Java, notamment l’importance de la copie profonde. Continuez votre apprentissage avec d’autres tutoriels Java.
Source:
https://www.digitalocean.com/community/tutorials/how-to-create-immutable-class-in-java