Unveränderliche Objekte in Java

1. Übersicht

In diesem Tutorial erfahren Sie, was ein Objekt unveränderlich macht, wie Unveränderlichkeit in Java erreicht wird und welche Vorteile dies mit sich bringt.

2. Was ist ein unveränderliches Objekt?

Ein unveränderliches Objekt ist ein Objekt, dessen interner Zustand nach seiner vollständigen Erstellung konstant bleibt .

Dies bedeutet, dass die öffentliche API eines unveränderlichen Objekts garantiert, dass es sich während seiner gesamten Lebensdauer gleich verhält.

Wenn wir uns die Klasse String ansehen , können wir sehen, dass sich der ursprüngliche String nicht ändert , selbst wenn seine API uns mit seiner Ersetzungsmethode ein veränderliches Verhalten zu bieten scheint :

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

Die API bietet schreibgeschützte Methoden. Sie sollte niemals Methoden enthalten, die den internen Status des Objekts ändern.

3. Das endgültige Schlüsselwort in Java

Bevor wir versuchen, Unveränderlichkeit in Java zu erreichen, sollten wir über das endgültige Schlüsselwort sprechen .

In Java sind Variablen standardmäßig veränderbar, dh wir können den Wert ändern, den sie enthalten .

Durch die Verwendung des Schlüsselworts final beim Deklarieren einer Variablen lässt der Java-Compiler den Wert dieser Variablen nicht ändern. Stattdessen wird ein Fehler bei der Kompilierung gemeldet:

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

Beachten Sie, dass final uns nur verbietet, die Referenz zu ändern, die die Variable enthält. Es schützt uns nicht davor, den internen Status des Objekts, auf das es verweist, mithilfe seiner öffentlichen API zu ändern:

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

Die zweite assertEquals schlägt fehl, da das Hinzufügen eines Elements zur Liste seine Größe ändert. Daher ist es kein unveränderliches Objekt.

4. Unveränderlichkeit in Java

Nachdem wir nun wissen, wie Änderungen am Inhalt einer Variablen vermieden werden können, können wir damit die API unveränderlicher Objekte erstellen.

Um die API eines unveränderlichen Objekts zu erstellen, müssen wir sicherstellen, dass sich sein interner Status nicht ändert, unabhängig davon, wie wir seine API verwenden.

Ein Schritt vorwärts in die richtige Richtung ist die Verwendung von final bei der Deklaration seiner Attribute:

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

Beachten Sie, dass Java uns garantiert, dass sich der Wert des Betrags nicht ändert. Dies ist bei allen primitiven Typvariablen der Fall.

In unserem Beispiel wird jedoch nur garantiert, dass sich die Währung nicht ändert. Daher müssen wir uns auf die Währungs- API verlassen, um sich vor Änderungen zu schützen .

In den meisten Fällen benötigen wir die Attribute eines Objekts, um benutzerdefinierte Werte zu speichern, und der Ort, an dem der interne Status eines unveränderlichen Objekts initialisiert werden kann, ist sein Konstruktor:

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

Wie bereits erwähnt, verfügt unsere Money- Klasse nur über schreibgeschützte Methoden , um die Anforderungen einer unveränderlichen API zu erfüllen .

Mit der Reflection-API können wir die Unveränderlichkeit aufheben und unveränderliche Objekte ändern. Die Reflexion verstößt jedoch gegen die öffentliche API des unveränderlichen Objekts. In der Regel sollten wir dies vermeiden.

5. Vorteile

Da der interne Zustand eines unveränderlichen Objekts zeitlich konstant bleibt, können wir ihn sicher auf mehrere Threads verteilen .

Wir können es auch frei verwenden, und keines der Objekte, die darauf verweisen, wird einen Unterschied bemerken. Wir können sagen, dass unveränderliche Objekte frei von Nebenwirkungen sind .

6. Fazit

Unveränderliche Objekte ändern ihren internen Zustand nicht rechtzeitig, sie sind threadsicher und frei von Nebenwirkungen. Aufgrund dieser Eigenschaften sind unveränderliche Objekte auch besonders nützlich, wenn es sich um Multithread-Umgebungen handelt.

Die in diesem Artikel verwendeten Beispiele finden Sie auf GitHub.