Das Problem, das Optional löst
Eine Methode, die womöglich keine Antwort hat, stand in Java schon immer vor einer unangenehmen Wahl: null zurückgeben und hoffen, dass der Aufrufer ans Prüfen denkt. Meist tut er es nicht, und das Ergebnis ist eine NullPointerException, die weit entfernt von der Methode auftaucht, die das null zurückgegeben hat.
Optional<T> ist eine kleine Box, die entweder einen Wert enthält oder explizit leer ist. Indem eine Methode Optional<User> statt User zurückgibt, sagt sie dem Aufrufer schon in ihrer Signatur „das findet möglicherweise nichts" - und der Compiler lenkt ihn dazu, diesen Fall zu behandeln.
Ein Optional erstellen
Es gibt drei Factory-Methoden, und die richtige zu wählen ist wichtig:
Optional.of(value)- der Wert darf nicht null sein, sonst wirft sie auf der Stelle eineNullPointerException.Optional.ofNullable(value)- leer, wenn der Wert null ist, sonst gefüllt. Verwende dies, um etwas zu umhüllen, das null sein könnte.Optional.empty()- ein leeres Optional.
Ein häufiger Fehler ist, bei einem Wert, der null sein könnte, zu Optional.of(x) zu greifen - das untergräbt den Zweck und wirft genau die Ausnahme, die du vermeiden wolltest. Im Zweifel verwende ofNullable.
Den Wert sicher auslesen
Sobald du ein Optional hast, besteht das Ziel darin, den Wert herauszuholen, ohne anzunehmen, dass er da ist. Das grobe Werkzeug ist get(), und du solltest es fast nie verwenden:
Optional<String> name = Optional.empty();
String value = name.get(); // wirft NoSuchElementException - nur eine getarnte NPE
Liefere stattdessen einen Ersatzwert oder reagiere auf das Vorhandensein. orElse gibt einen Standardwert zurück, wenn es leer ist; ifPresent führt Code nur aus, wenn ein Wert existiert:
Verwende orElseGet(supplier) statt orElse, wenn der Standardwert teuer zu erzeugen ist - der Supplier läuft nur, wenn das Optional tatsächlich leer ist. Das Argument von orElse wird immer ausgewertet, selbst wenn es nicht gebraucht wird.
orElseThrow für „das sollte existieren"
Manchmal ist leer wirklich ein Fehler - ein erforderlicher Konfigurationswert, ein Nutzer, der in der Datenbank sein sollte. orElseThrow verwandelt ein leeres Optional in eine klare Ausnahme deiner Wahl:
Das liest sich weitaus besser als ein if (opt.isPresent())-Block und macht den Fehlschlag genau an der Stelle explizit, an der er passiert.
Transformieren mit map und filter
Der eigentliche Gewinn ist das Verketten. map wendet eine Funktion nur dann auf den Wert an, wenn er vorhanden ist, und lässt ein leeres Optional leer - du transformierst also, ohne je einen null anzufassen. filter verwirft den Wert, wenn er einen Test nicht besteht.
Wenn deine Mapping-Funktion selbst ein Optional zurückgibt, verwende flatMap statt map, um ein umständliches Optional<Optional<T>> zu vermeiden. Das spiegelt die Unterscheidung zwischen map/flatMap wider, die du bei Streams gesehen hast - ein Optional verhält sich sehr ähnlich wie ein Stream aus null oder einem Element.
Wohin Optional gehört (und wohin nicht)
Optional ist als Rückgabetyp für Methoden gedacht, die legitimerweise kein Ergebnis liefern können - die klassischen Beispiele sind Stream.findFirst(), Suchen im Map-Stil und das Parsen. Verwende es dort, und deine API dokumentiert ihre eigenen Lücken.
Es ist nicht gedacht für:
- Felder. Es ist nicht serialisierbar und fügt pro Feld ein Objekt hinzu. Verwende einfache Referenzen und validiere im Konstruktor.
- Methodenparameter. Der Aufrufer muss dann die Argumente in
Optional.of(...)verpacken, was lauter ist als eine Überladung oder ein nullbarer Parameter. - Sammlungen. Gib eine leere
Listzurück, keinOptional<List>. Eine leere Sammlung bedeutet bereits „nichts".
Als Rückgabetyp behandelt, verwandelt Optional stille null-Bugs in Aufforderungen zur Kompilierzeit, den fehlenden Fall zu behandeln.
Als Nächstes: Ausnahmen
Optional behandelt den alltäglichen Fall „es gibt möglicherweise keinen Wert" sauber, aber manche Fehler sind wirklich außergewöhnlich - eine Datei, die sich nicht öffnen lässt, ein Netzwerk, das abbricht, eine Eingabe, die sich nicht parsen lässt. Java modelliert diese mit Ausnahmen, und das ist die nächste Seite.
Häufig gestellte Fragen
Was ist Optional in Java und warum sollte man es verwenden?
Optional<T> ist ein Container, der entweder einen Wert enthält oder leer ist. Es existiert, um im Rückgabetyp einer Methode explizit zu machen, dass „es möglicherweise keinen Wert gibt", damit Aufrufer dazu angehalten werden, den leeren Fall zu behandeln, anstatt eine null-Prüfung zu vergessen und zur Laufzeit auf eine NullPointerException zu stoßen. Verwende es vor allem als Rückgabetyp für Methoden, die kein Ergebnis liefern könnten, etwa eine Suche, die nichts findet.
Was ist der Unterschied zwischen Optional.of und Optional.ofNullable?
Optional.of(x) wirft sofort eine NullPointerException, wenn x null ist - verwende es, wenn du weißt, dass der Wert nicht null ist. Optional.ofNullable(x) gibt ein leeres Optional zurück, wenn x null ist, sonst ein gefülltes - verwende es, wenn der Wert null sein könnte. Optional.empty() liefert dir direkt ein leeres Optional.
Sollte ich Optional.get() aufrufen, um den Wert auszulesen?
Vermeide das rohe get() - es wirft eine NoSuchElementException, wenn das Optional leer ist, was nur eine NullPointerException unter anderem Namen ist. Bevorzuge orElse, orElseGet, orElseThrow, ifPresent oder map, damit der leere Fall immer behandelt wird. Wenn du zuerst prüfen musst, sichere dich mit isPresent() ab, aber die funktionalen Methoden sind sauberer.