Java – immutable Objects

Wenn wir Software entwickeln, ist eines unserer Ziele unvorhersehbare Seiteneffekte zu vermeiden. Ich war mal vor knapp zehn Jahren in einem Projekt, in dem ein Kollege bei aufgetauchten Fehlern im System zum Auftraggeber sagte „Oh! Das ist leider ein Seiteneffekt.“. Irgendwann meinte der Auftraggeber genervt „Ich will das Wort Seiteneffekt nicht mehr hören!“.

Wie kann man solche Fälle minimieren? Ein Punkt ist die richtige Datenkapselung, wie ich hier bereits zeigte. Ein weiterer Punkt ist, Daten einfach unveränderlich zu machen. Denn wodurch entstehen mysteriöse Seiteneffekte? Wenn sich Objekte im Wert ändern, ohne das man es selbst veranlasst oder mitbekommen hat!

In funktionalen Programmiersprachen (z.B. OCaml) gibt es deshalb keine Variablen. Zumindest nicht welche, die variabel sind. Wenn X = 1 ist, dann bleibt es das auch. Und A(1) liefert deshalb immer das gleiche Ergebnis. Es gibt keine Seiteneffekte.

Read-Only ist zu wenig

Das kann man auch in Java, C++ u.a. imperativen Sprachen erreichen. Dazu definieren wir uns einfach Immutable Objects, sprich unveränderliche Objekte. Diese dürfen nicht mit Read-Only (Nur-Lesend) Objekte verwechselt werden. Denn Read-Only-Objekte können sehr wohl ihren Zustand ohne Eingriff von Außen ändern. Immutable Objekte behalten dagegen nach der Konstruktion ihren Zustand/Wert… X = 1 bleibt halt 1.

Nebenbei: ein weiterer Vorteil ist die Multithread-Sicherheit zum Nulltarif!

Value Objects

Wir können wunderbar sogenannte Value Objects (Wertobjekte) dafür verwenden. Values können anhand ihres Wertes identifiziert werden, und nicht anhand einer ID. Die Zahl 1 identifizieren wir anhand ihres Wertes. Die Farbe Rot identifizieren wir anhand ihres Farbwertes. usw. Und sie ändern sich auch nicht.

Ich habe in meinem aktuellen Projekt für die Eigenschaft eines speziellen DesktopPanels eine Farbe setzen wollen. Deshalb habe ich mir einen eigenen Value definiert: DesktopColor.

Denn die Klasse Color aus der Java Runtime Library ist veränderbar. Weiterhin ist sie nicht typsicher genug. (dazu ein anderes Mal mehr)

Immutable Class

Wie muss eine solche Klasse definiert werden?

  1. Die Klasse muss final sein, damit sie nicht vererbt werden kann.
  2. Alle Member-Variablen müssen privat und final sein.
  3. Die Konstruktion muss in einem Schritt erfolgen, es darf für den Aufrufer keine separate Init-Methode o.ä. geben.
  4. Natürlich darf es keine Setter-Methoden geben.
  5. Wenn es mutable Member-Variablen gibt, müssen diese per Defensiver Kopie angelegt und zurück gegeben werden.
public final class DesktopColor {
  private final Color m_color; // mutable
  public final DesktopColor(Color c) {
    m_color = new Color(c); // defensive Copy
  }
  public getColor() {
    return new Color(color); // defensive Copy
  }
}

Ich habe damit mehrere Fliegen mit einer Klappe geschlagen! Die Farbe in meinem Desktop Panel ist typisiert, ist Thread-sicher und der Aufrufer/Benutzer muss immer die Setter in meinem DesktopPanel benutzen damit es keine Seiteneffekte gibt.

Fazit

Sie sollten versuchen so viele Klassen als Immutable zu definieren wie möglich. Es gibt keinen Grund vorsorglich z.B. Setter-Methoden zu definieren, obwohl sie noch gar nicht nötig sind. Besonders Klassen mit wenigen Variablen können sich als Value heraus stellen.