Wie kann man eine Java-Klasse unveränderlich machen, was ist die Notwendigkeit der Unveränderlichkeit und gibt es einen Vorteil, wenn man diese verwendet?
Wie kann man eine Java-Klasse unveränderlich machen, was ist die Notwendigkeit der Unveränderlichkeit und gibt es einen Vorteil, wenn man diese verwendet?
@Immutable
Anmerkung von jcabi-Aspekten
Antworten:
Was ist ein unveränderliches Objekt?
Ein unveränderliches Objekt ändert seinen Status nicht, nachdem es instanziiert wurde.
Wie mache ich ein Objekt unveränderlich?
Im Allgemeinen kann ein unveränderliches Objekt erstellt werden, indem eine Klasse definiert wird, für die keines ihrer Mitglieder verfügbar ist und für die keine Setter vorhanden sind.
Die folgende Klasse erstellt ein unveränderliches Objekt:
class ImmutableInt {
private final int value;
public ImmutableInt(int i) {
value = i;
}
public int getValue() {
return value;
}
}
Wie im obigen Beispiel zu sehen ist, kann der Wert von ImmutableInt
nur festgelegt werden, wenn das Objekt instanziiert wird, und wenn nur ein Getter ( getValue
) vorhanden ist, kann der Status des Objekts nach der Instanziierung nicht geändert werden.
Es muss jedoch darauf geachtet werden, dass alle Objekte, auf die das Objekt verweist, ebenfalls unveränderlich sind. Andernfalls kann der Status des Objekts möglicherweise geändert werden.
Wenn Sie beispielsweise ArrayList
zulassen , dass ein Verweis auf ein Array oder über einen Getter abgerufen wird, kann sich der interne Status durch Ändern des Arrays oder der Sammlung ändern:
class NotQuiteImmutableList<T> {
private final List<T> list;
public NotQuiteImmutableList(List<T> list) {
// creates a new ArrayList and keeps a reference to it.
this.list = new ArrayList(list);
}
public List<T> getList() {
return list;
}
}
Das Problem mit dem obigen Code ist, dass das ArrayList
durch erhalten getList
und manipuliert werden kann, was dazu führt, dass der Zustand des Objekts selbst geändert wird, daher nicht unveränderlich.
// notQuiteImmutableList contains "a", "b", "c"
List<String> notQuiteImmutableList= new NotQuiteImmutableList(Arrays.asList("a", "b", "c"));
// now the list contains "a", "b", "c", "d" -- this list is mutable.
notQuiteImmutableList.getList().add("d");
Eine Möglichkeit, dieses Problem zu umgehen, besteht darin, eine Kopie eines Arrays oder einer Sammlung zurückzugeben, wenn diese von einem Getter aufgerufen wird:
public List<T> getList() {
// return a copy of the list so the internal state cannot be altered
return new ArrayList(list);
}
Was ist der Vorteil der Unveränderlichkeit?
Der Vorteil der Unveränderlichkeit liegt in der Parallelität. Es ist schwierig, die Korrektheit in veränderlichen Objekten aufrechtzuerhalten, da mehrere Threads versuchen könnten, den Status desselben Objekts zu ändern, was dazu führt, dass einige Threads einen unterschiedlichen Status desselben Objekts sehen, abhängig vom Zeitpunkt der Lese- und Schreibvorgänge in das Objekt Objekt.
Durch ein unveränderliches Objekt kann sichergestellt werden, dass alle Threads, die das Objekt betrachten, denselben Status sehen, da sich der Status eines unveränderlichen Objekts nicht ändert.
return Collections.unmodifiableList(list);
eine schreibgeschützte Ansicht der Liste zurückgeben.
final
werden. Andernfalls kann es mit einer Setter-Methode (oder anderen Arten von Mutationsmethoden und veränderlichen Feldern) erweitert werden.
final
wenn die Klasse nur private
Felder hat , da von Unterklassen aus nicht auf sie zugegriffen werden kann.
Zusätzlich zu den bereits gegebenen Antworten würde ich empfehlen, über Unveränderlichkeit in Effective Java, 2. Ausgabe, zu lesen, da einige Details leicht zu übersehen sind (z. B. defensive Kopien). Plus, effektive Java 2nd Ed. ist ein Muss für jeden Java-Entwickler.
Sie machen eine Klasse unveränderlich wie folgt:
public final class Immutable
{
private final String name;
public Immutable(String name)
{
this.name = name;
}
public String getName() { return this.name; }
// No setter;
}
Im Folgenden sind die Anforderungen aufgeführt, um eine Java-Klasse unveränderlich zu machen:
final
(damit untergeordnete Klassen nicht erstellt werden können)final
(damit wir den Wert nach der Objekterstellung nicht ändern können)Unveränderliche Klassen sind nützlich, weil
- sie threadsicher sind.
- Sie drücken auch etwas Tiefes an Ihrem Design aus: "Kann das nicht ändern.", Wenn es zutrifft, ist es genau das, was Sie brauchen.
Unveränderlichkeit kann hauptsächlich auf zwei Arten erreicht werden:
final
Instanzattributen, um eine Neuzuweisung zu vermeidenVorteile der Unveränderlichkeit sind die Annahmen, die Sie für dieses Objekt treffen können:
Unveränderliche Klassen können nach der Instanziierung keine Werte neu zuweisen. Der Konstruktor weist seinen privaten Variablen Werte zu. Bis das Objekt null wird, können Werte aufgrund der Nichtverfügbarkeit von Setter-Methoden nicht geändert werden.
unveränderlich zu sein sollte folgendes befriedigen,
/**
* Strong immutability - by making class final
*/
public final class TestImmutablity {
// make the variables private
private String Name;
//assign value when the object created
public TestImmutablity(String name) {
this.Name = name;
}
//provide getters to access values
public String getName() {
return this.Name;
}
}
Vorteile: Unveränderliche Objekte enthalten ihre initialisierten Werte, bis sie sterben.
Unveränderliche Klassen sind solche, deren Objekte nach der Erstellung nicht mehr geändert werden können.
Unveränderliche Klassen sind nützlich für
Beispiel
String-Klasse
Codebeispiel
public final class Student {
private final String name;
private final String rollNumber;
public Student(String name, String rollNumber) {
this.name = name;
this.rollNumber = rollNumber;
}
public String getName() {
return this.name;
}
public String getRollNumber() {
return this.rollNumber;
}
}
@ Jack, endgültige Felder und Setter in der Klasse zu haben, macht die Klasse nicht unveränderlich. Das endgültige Schlüsselwort stellt nur sicher, dass eine Variable niemals neu zugewiesen wird. Sie müssen eine tiefe Kopie aller Felder in Getter-Methoden zurückgeben. Dadurch wird sichergestellt, dass nach dem Abrufen eines Objekts von der Getter-Methode der interne Status des Objekts nicht gestört wird.
Eine andere Möglichkeit, unveränderliche Objekte zu erstellen, ist die Verwendung der Immutables.org- Bibliothek:
Angenommen, die erforderlichen Abhängigkeiten wurden hinzugefügt, erstellen Sie eine abstrakte Klasse mit abstrakten Zugriffsmethoden. Sie können dasselbe tun, indem Sie mit Schnittstellen oder sogar Anmerkungen (@interface) Anmerkungen machen:
package info.sample;
import java.util.List;
import java.util.Set;
import org.immutables.value.Value;
@Value.Immutable
public abstract class FoobarValue {
public abstract int foo();
public abstract String bar();
public abstract List<Integer> buz();
public abstract Set<Long> crux();
}
Es ist jetzt möglich, die generierte unveränderliche Implementierung zu generieren und dann zu verwenden:
package info.sample;
import java.util.List;
public class FoobarValueMain {
public static void main(String... args) {
FoobarValue value = ImmutableFoobarValue.builder()
.foo(2)
.bar("Bar")
.addBuz(1, 3, 4)
.build(); // FoobarValue{foo=2, bar=Bar, buz=[1, 3, 4], crux={}}
int foo = value.foo(); // 2
List<Integer> buz = value.buz(); // ImmutableList.of(1, 3, 4)
}
}
Als nicht-englischer Muttersprachler mag ich die übliche Interpretation von "unveränderlicher Klasse" als "konstruierte Klassenobjekte sind unveränderlich" nicht. Vielmehr neige ich selbst dazu, dies als "das Klassenobjekt selbst ist unveränderlich" zu interpretieren.
Das heißt, "unveränderliche Klasse" ist eine Art unveränderliches Objekt. Der Unterschied besteht darin, zu beantworten, was der Nutzen ist. Meines Wissens / meiner Interpretation nach verhindert eine unveränderliche Klasse, dass ihre Objekte Änderungen am Laufzeitverhalten vornehmen.
Eine unveränderliche Klasse ist einfach eine Klasse, deren Instanzen nicht geändert werden können.
Alle in jeder Instanz enthaltenen Informationen sind für die Lebensdauer des Objekts festgelegt, sodass niemals Änderungen beobachtet werden können.
Unveränderliche Klassen sind einfacher zu entwerfen, zu implementieren und zu verwenden als veränderbare Klassen.
Befolgen Sie diese fünf Regeln, um eine Klasse unveränderlich zu machen:
Geben Sie keine Methoden an, die den Status des Objekts ändern
Stellen Sie sicher, dass die Klasse nicht erweitert werden kann.
Machen Sie alle Felder endgültig.
Machen Sie alle Felder privat.
Stellen Sie den exklusiven Zugriff auf veränderbare Komponenten sicher.
Unveränderliche Objekte sind von Natur aus threadsicher. Sie erfordern keine Synchronisation.
Unveränderliche Objekte können frei geteilt werden.
Unveränderliche Objekte sind großartige Bausteine für andere Objekte
Die meisten Antworten hier sind gut, und einige haben die Regeln erwähnt, aber ich finde es gut, in Worte zu fassen, warum und wann wir diese Regeln befolgen müssen. Also gebe ich die folgende Erklärung
Und wenn wir versuchen, Setter für diese endgültigen Variablen zu verwenden, gibt der Compiler natürlich einen Fehler aus.
public class ImmutableClassExplored {
public final int a;
public final int b;
/* OR
Generally we declare all properties as private, but declaring them as public
will not cause any issues in our scenario if we make them final
public final int a = 109;
public final int b = 189;
*/
ImmutableClassExplored(){
this. a = 111;
this.b = 222;
}
ImmutableClassExplored(int a, int b){
this.a = a;
this.b= b;
}
}
Müssen wir die Klasse als "endgültig" deklarieren?
1. Nur primitive Mitglieder haben: Wir haben kein Problem Wenn die Klasse nur primitive Mitglieder hat, müssen wir die Klasse nicht als endgültig deklarieren.
2. Objekte als Mitgliedsvariablen haben: Wenn wir Objekte als Mitgliedsvariablen haben, müssen wir die Mitglieder dieser Objekte ebenfalls endgültig machen. Das heißt, wir müssen tief im Baum durchqueren und alle Objekte / Grundelemente als endgültig festlegen, was möglicherweise nicht immer möglich ist. Die Problemumgehung besteht also darin, die Klasse endgültig zu machen, wodurch die Vererbung verhindert wird. Es ist also keine Frage, ob Unterklassen Getter-Methoden überschreiben.
Wie kann man eine Java-Klasse unveränderlich machen?
Ab JDK 14+ mit JEP 359 können wir " records
" verwenden. Es ist die einfachste und stressfreieste Art, unveränderliche Klassen zu erstellen.
Eine Datensatzklasse ist ein flach unveränderlicher , transparenter Träger für einen festen Satz von Feldern, der als Datensatz bezeichnet components
wird und eine state
Beschreibung für den Datensatz enthält. Jedes component
führt zu einem final
Feld, das den angegebenen Wert enthält, und einer accessor
Methode zum Abrufen des Werts. Der Feldname und der Zugriffsname stimmen mit dem Namen der Komponente überein.
Betrachten wir das Beispiel zum Erstellen eines unveränderlichen Rechtecks
record Rectangle(double length, double width) {}
Keine Notwendigkeit, einen Konstruktor zu deklarieren, keine Notwendigkeit, equals & hashCode-Methoden zu implementieren. Nur alle Datensätze benötigen einen Namen und eine Statusbeschreibung.
var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1
Wenn Sie den Wert während der Objekterstellung überprüfen möchten, müssen wir den Konstruktor explizit deklarieren.
public Rectangle {
if (length <= 0.0) {
throw new IllegalArgumentException();
}
}
Der Datensatzkörper kann statische Methoden, statische Felder, statische Initialisierer, Konstruktoren, Instanzmethoden und verschachtelte Typen deklarieren.
Instanzmethoden
record Rectangle(double length, double width) {
public double area() {
return this.length * this.width;
}
}
statische Felder, Methoden
Da state Teil der Komponenten sein sollte, können wir Datensätzen keine Instanzfelder hinzufügen. Wir können jedoch statische Felder und Methoden hinzufügen:
record Rectangle(double length, double width) {
static double aStaticField;
static void aStaticMethod() {
System.out.println("Hello Static");
}
}
Was ist das Bedürfnis nach Unveränderlichkeit und hat es einen Vorteil, dies zu nutzen?
Zuvor veröffentlichte Antworten sind gut genug, um die Notwendigkeit der Unveränderlichkeit und ihrer Vorteile zu rechtfertigen