Inleiding
Dit artikel geeft een overzicht van hoe je een onveranderlijke klasse kunt maken in Java-programmering.
Een object is onveranderlijk wanneer de toestand ervan niet verandert nadat het is geïnitialiseerd. Bijvoorbeeld, String
is een onveranderlijke klasse en, eenmaal geïnstantieerd, verandert de waarde van een String
-object nooit. Leer meer over waarom de klasse String
onveranderlijk is in Java.
Omdat een onveranderlijk object niet kan worden bijgewerkt, moeten programma’s voor elke verandering van toestand een nieuw object maken. Echter, onveranderlijke objecten hebben ook de volgende voordelen:
- Een onveranderlijke klasse is goed voor cachingdoeleinden omdat je je geen zorgen hoeft te maken over waardeveranderingen.
- Een onveranderlijke klasse is inherent thread-safe, dus je hoeft je geen zorgen te maken over threadveiligheid in multi-threaded omgevingen.
Leer meer over multi-threading in Java en bekijk de Java Multi-Threaded Interview Questions.
Het creëren van een Onveranderlijke Klasse in Java
Om een onveranderlijke klasse in Java te creëren, moet je deze algemene principes volgen:
- Declareren van de klasse als
final
zodat deze niet uitgebreid kan worden. - Maak alle velden
private
zodat directe toegang niet is toegestaan. - Bied geen setter-methoden voor variabelen.
- Maak alle mutabele velden
final
zodat de waarde van een veld slechts één keer kan worden toegewezen. - Initialiseer alle velden met behulp van een constructor-methode die diepe kopieën uitvoert.
- Voer kloonbewerkingen uit van objecten in de getter-methoden om een kopie terug te geven in plaats van de werkelijke objectreferentie.
De volgende klasse is een voorbeeld dat de basisprincipes van onveranderlijkheid illustreert. De klasse FinalClassExample
definieert de velden en biedt de constructor-methode die diepe kopieën gebruikt om het object te initialiseren. De code in de main
-methode van het bestand FinalClassExample.java
test de onveranderlijkheid van het object.
Maak een nieuw bestand genaamd FinalClassExample.java
aan en kopieer de volgende code:
import java.util.HashMap;
import java.util.Iterator;
public final class FinalClassExample {
// velden van de FinalClassExample-klasse
private final int id;
private final String name;
private final HashMap<String,String> testMap;
public int getId() {
return id;
}
public String getName() {
return name;
}
// Getter-functie voor wijzigbare objecten
public HashMap<String, String> getTestMap() {
return (HashMap<String, String>) testMap.clone();
}
// Constructor-methode voor diepe kopie
public FinalClassExample(int i, String n, HashMap<String,String> hm){
System.out.println("Performing Deep Copy for Object initialization");
// Het "this"-woord verwijst naar het huidige object
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;
}
// Test de onveranderlijke klasse
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);
// print de ce-waarden
System.out.println("ce id: "+ce.getId());
System.out.println("ce name: "+ce.getName());
System.out.println("ce testMap: "+ce.getTestMap());
// wijzig de waarden van de lokale variabele
i=20;
s="modified";
h1.put("3", "third");
// print de waarden opnieuw
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());
}
}
Compileer en voer het programma uit:
- javac FinalClassExample.java
- java FinalClassExample
Opmerking: U kunt mogelijk het volgende bericht krijgen bij het compileren van het bestand: Let op: FinalClassExample.java maakt gebruik van ongecontroleerde of onveilige bewerkingen
omdat de getter-methode een ongecontroleerde cast gebruikt van HashMap<String,String>
naar Object
. U kunt de compilerwaarschuwing negeren voor dit voorbeeld.
U krijgt de volgende uitvoer:
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}
De uitvoer toont aan dat de waarden van de HashMap niet zijn gewijzigd omdat de constructor een diepe kopie gebruikt en de getter-functie een kloon van het originele object retourneert.
Wat gebeurt er wanneer je geen diepe kopie en klonen gebruikt?
Je kunt wijzigingen aanbrengen in het bestand FinalClassExample.java
om te laten zien wat er gebeurt wanneer je in plaats van een diepe kopie een ondiepe kopie gebruikt en het object retourneert in plaats van een kopie. Het object is niet langer onveranderlijk en kan worden gewijzigd. Voer de volgende wijzigingen door in het voorbeeldbestand (of kopieer en plak uit het codevoorbeeld):
- Verwijder de constructor-methode die een diepe kopie levert en voeg de constructor-methode toe die een ondiepe kopie levert, zoals gemarkeerd in het volgende voorbeeld.
- In de getter-functie, verwijder
return (HashMap<String, String>) testMap.clone();
en voegreturn testMap;
toe.
Het voorbeeldbestand zou er nu zo uit moeten zien:
import java.util.HashMap;
import java.util.Iterator;
public final class FinalClassExample {
// velden van de klasse 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;
}
// Getter-functie voor veranderlijke objecten
public HashMap<String, String> getTestMap() {
return testMap;
}
// Constructor-methode voor het uitvoeren van een ondiepe kopie
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;
}
// Test de onveranderlijke klasse
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);
// druk de ce-waarden af
System.out.println("ce id: "+ce.getId());
System.out.println("ce name: "+ce.getName());
System.out.println("ce testMap: "+ce.getTestMap());
// wijzig de waarden van de lokale variabele
i=20;
s="modified";
h1.put("3", "third");
// druk de waarden opnieuw af
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());
}
}
Compileer en voer het programma uit:
- javac FinalClassExample.java
- java FinalClassExample
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}
Het resultaat toont aan dat de waarden van de HashMap zijn gewijzigd omdat de constructor-methode een ondiepe kopie gebruikt, waardoor er een directe verwijzing is naar het oorspronkelijke object in de getter-functie.
Conclusie
Je hebt enkele algemene principes geleerd om te volgen bij het maken van onveranderlijke klassen in Java, inclusief het belang van diepe kopie. Ga verder met leren met meer Java tutorials.
Source:
https://www.digitalocean.com/community/tutorials/how-to-create-immutable-class-in-java