Total Optional

Hallo Spaß-Coder.

Java 8 ist mittlerweile einige Tage verfügbar, aber dennoch möchten wir heute auf ein darin enthaltenes Feature schauen, welches wir (und vielleicht auch ihr) bisher selten genutzt haben (habt).

Arbeitet ihr auch gerne mit Methoden, die ggf. Null zurückgeben, wenn z.B. ein gesuchtes Element nicht gefunden wurde oder die Operation unvollständig beendet wurde? Also ich finde es immer wieder umständlich und unschön explizit auf Null zu prüfen bevor ich meine gewünschte Verarbeitung auf dem Rückgabewert durchführe.

Null-Prüfung am Beispiel

Für diesen Artikel schauen wir uns das Priority-Queue Kata[1][2] an. Dabei soll eine Prioritäten-Warteschlange implementiert werden. Wie soll die Methode dequeue(), welche das erste Element der Warteschlange zurück gibt, reagieren, wenn noch keine Elemente enthalten sind? Hier sind spontan zwei Verhaltensweisen möglich: das Werfen einer Ausnahme oder Rückgabe von Null.

Angenommen, unsere interne Datrenstruktur der Klasse PriorityQueue<T> ist eine ArrayList mit einem Paar aus dem tatsächlichen Element und der Priorität.

Ist die Länge der Warteschlange bisher gleich 0 (also leer), dann geben wir Null zurück. Sonst holen wir das erste Element und geben den Wert zurück, welche in unserem Fall der Schlüssel des Paars ist.

Die Verwendung der Klasse PriorityQueue könnte nun so aussehen:

Dies soll natürlich nur verdeutlichen, dass Null als möglicher Rückgabewert grundsätzlich gesondert behandelt werden muss. Offensichtlich bläht die Null-Abfrage unseren Code auf in nimmt fünf Zeilen in Anspruch. Wie sieht die Alternative aus?

Optional mit Java 8

Die Klasse Optional im Paket java.util ist ein Wrapper, also ein Container, der jede andere Klasse einhüllen kann. Anschließend kann ich den Container fragen, ob diese ein Element enthält und damit arbeiten. Oder, falls der Container leer ist, entsprechend darauf reagieren – und das alles sehr kompakt.

Ändern wir unsere Methode dequeue() so ab, dass der Rückgabewert in den Optional-Container gehüllt wird.

Der Rückgabewert ist nun von T geändert in Optional<T>. Damit ist unser Container typisiert. Die Idee ist, einen leeren Container zurück zu geben, wenn unsere Warteschlange noch keine Elemente enthält. Was wir damit erreichen und machen können, sehen wir weiter unten.

Ein leerer Container wird mit Optional.empty() erzeugt. Ist die Länge der Warteschlange größer 0, so entnehmen wir das erste Element und erzeugen einen Container mit dem Wert dieses Elements. Hier haben wir zwei Möglichkeiten, Objekte in einen Container einzuhüllen.

  1. Optional.of(T value) – hier ist wichtig, dass unser Objekt nicht Null ist, sonst gibt es eine NullPointerException
  2. Optional.ofNullable(T value) – hier dürfen wir auch ein Null-Objekt in den Container legen

Dann geben wir den Container zurück, der entweder leer ist oder den Wert unseres ersten Elements der Warteschlange enthält.

Das Arbeiten mit dem Rückgabewert unserer Methode kann nun Dank des Containers deutlich vereinfacht werden.

Wir fragen den Container einfach, ob es ein Nicht-Null-Objekt enthält. Einfach, oder? Aber…ist das nun viel kürzer als vorher? Das geht noch besser!

Ahhh, das ist kurz – aber was macht es? Die Methode ifPresent prüft, ob ein Nicht-Null-Objekt im Container enthalten ist und führt dann den angegeben Methodenaufruf aus, in diesem Fall wird von System.out die Methoden println aufgerufen (zur Schreibweise siehe auch Methodenreferenz). Damit wird, falls vorhanden, unser erstes Element ausgegeben. Andernfalls geschieht nichts.

Wollen wir das Objekt aus dem Container abrufen und sind uns sicher, dass der Container nicht Null enthält, ist dies in dieser Form möglich.

Enthält der Container jedoch Null, wird beim Aufruf der Methode get() eine NoSuchElementException geworfen. Dies kann durch eine vorhergehende Prüfung per isPresent() abgesichert werden, da hierbei true zurückgegeben wird, falls Nicht-Null enthalten ist und false andernfalls.

Immer wieder gibt es die Notwendigkeit, das Objekt selbst oder ein Default-Objekt zurück zu geben. Dies sieht mit Optional so aus:

Wenn ein Objekt ungleich Null enthalten ist, wird dieses zurück gegeben, andernfalls der angegebene Default, in diesem Fall 5.

Soll statt der Rückgabe des Defaults eine Ausnahme geworfen werfen, ist auch das möglich.

Hier wird, falls von der Methode dequeue() Null zurück gegeben wird, eine NullPointerException geworfen.

Unsere Erfahrungen mit Optional

Wie bereits erwähnt haben wir bisher eher selten Optional verwendet. Allerdings ist dies unserer Ansicht nach eine mächtige Erweiterung von Java mit der unser Code an einigen Stellen aufgeräumt werden kann. Ggf. werden wir besonders schöne Anwendungsfälle mit Optional nachträglich noch ergänzen.

Zusammenfassung

Optional in Java 8 ist eine schicke Erweiterung für den Umgang mit Null. Lästige If-Statements zur Prüfung auf Null vor dem Zugriff auf das jeweilige Objekt können damit elegant und lesbar verpackt werden. Sicherlich sollte Optional nicht einfach überall verwendet werden. Insbesondere bei Rückgabewerten von Methoden kann dies jedoch hilfreich sein.

Viel Spaß beim Ausprobieren.

Eure Spaß-Coder

Links zum Thema:

  • Kata Priority Queue, http://ccd-school.de/coding-dojo/#cd3
  • Queue als Datenstruktur, https://de.wikipedia.org/wiki/Warteschlange_%28Datenstruktur%29
  • Java-Doc von Optional, https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert