Was ist ein Optional? v.8
Motivation
Die Sprachstruktur von Java verlangte bislang die häufige und
fehleranfällige Überprüfung darauf, ob ein Wert null
annehmen kann. Die Möglichkeit, Werte mit Hilfe der Klasse java.util.Optional<T>
zu kapseln, kann dies, wenn auch sicherlich nicht in allen, so doch
in vielen Fällen deutlich vereinfachen, bzw.
überflüssig machen. Die Verwendung von Optionals
führt zu einem kürzeren und übersichtlicheren Code
und zwingt den Programmierer zudem, sich mit evtl. fehlenden Werten
zu beschäftigen.
Ein Optional
ist eine Objekt, das man sich als
Datenbehälter vorstellen kann, der entweder einen Wert
enthält oder leer (empty) ist. Leer ist hier auch nicht
gleichbedeutend mit null
!
Oracle empfiehlt, die Synchronisation, Referenzvergleiche mit ==
und andere Prüfungen auf die Identität von Optional
-Objekten
zu vermeiden.
Optional erzeugen
Optionals können auf mehrere Weisen erzeugt werden:
Optional<String> oe = Optional.empty(); // leeres Optional Optional<String> os = Optional.of("Hallo Welt!"); // enthält den String "Hallo Welt!"
Doch Vorsicht! Werte dürfen auf diese Weise nicht null
sein. Sollte das der Fall sein, so muss zum Erzeugen des Objektes
die Methode ofNullable()
verwendet werden:
Optional<String> on = Optional.of(null); // NullPointerException Optional<String> onb = Optional.ofNullable(null); // leeres Optional
Der Zugriff auf die Werte
Der Zugriff auf den Inhalt des in einem Optional
gekapselten Wertes kann durch die Methode get()
erfolgen:
Optional<String> os = Optional.of("Hallo Welt!"); System.out.println(os.get()); // Hallo Welt
Bei leerem oder mit null
initialisiertem Optional
wird allerdings eine NoSuchElementException
geworfen:
Optional<String> onb = Optional.ofNullable(null); System.out.println(onb.get()); // NoSuchElementException
Um dies zu verhindern kann durch isPresent()
überprüft werden, ob ein Optional
leer ist.
v.11Ab Java 11 kann dies auch durch isEmpty()
erfolgen. Die Methode funktioniert umgekehrt. Sie liefert true
,
wenn das Optional
leer ist.
Optional<String> onb = Optional.ofNullable(null); System.out.println(onb.isPresent()); // false System.out.println(onb.isEmpty()); // true ab Java 11
Allerdings bieten sich zur Überprüfung hier oft andere
Vorgehensweisen an, da der boolsche Rückgabewert ggf. wieder
umständlich validiert werden muss.
Stattdessen könnte
z.B. vielmehr ein Default-Wert angegeben werden, der bei leerem
Objekt greift. Die Methoden or()
, orElse()
und orElseGet()
ermöglichen das.
Die
Unterschiede zwischen den Methoden sind klein aber fein und
müssen besonders bei zeitkritischen Anwendungen
berücksichtigt werden. orElse()
erwartet ein
Objekt als Parameter und gibt dies zurück, falls das Optional
leer sein sollte. Das Objekt wird somit in jedem Falle gebildet und
erst danach entscheidet sich, ob es weiter verarbeitet wird oder
nicht.
Optional<String> onb = Optional.ofNullable(null); System.out.println(onb.orElse("Hallo Paul!")); // Hallo Paul!
orElseGet()
erwartet einen Supplier
als
Parameter, dessen Methode einen Wert liefert. D.h., dass im Falle
eines nicht leeren Optional
, die Methode des Suppliers
nicht aufgerufen wird. Ein ggf. zurückzugebendes Objekt muss
somit gar nicht erst gebildet werden.
Eine Ergänzung in
Java 9 ist die Methode or()
, die ebenfalls einen Supplier
erwartet. Die Rückgabe besteht jedoch wiederum in einem Optional
.
class OptElseGetClass { public OptElseGetClass () { Optional<String> s = Optional.ofNullable(null); System.out.println(s.orElseGet(this::print)); // Optional ist leer System.out.println(s.or(this::printOpt).get()); // Optional ist immer noch leer! } public String print() { return "Optional ist leer"; } public Optional<String> printOpt() { return Optional.of("Optional ist immer noch leer!"); } }
Eine weitere Möglichkeit des Umgangs mit einem ggf. leeren Optional
-Objekt
bietet die Methode ifPresent()
(nicht isPresent
!),
die ein Consumer
-Interface als Parameter erwartet und
dies ausführt falls das Optional
nicht leer ist.
Im anderen Fall geschieht nichts.
Optional<String> onb = Optional.ofNullable(null); onb.ifPresent(s -> System.out.println("Inhalt vorhanden!")); // keine Ausgabe
Seit Java 9 kann für den Fall eines leeren Optional
auch eine Alternative erzeugt werden. Die Methode ifPresentOrElse()
erwartet zwei Interfaces als Parameter: einen Consumer
und ein Runnable
.
Optional<String> onb = Optional.ofNullable(null); onb.ifPresentOrElse(s -> System.out.println("Inhalt vorhanden!"),getWarning());
Die Methode getWarning()
des Beispiels muss ein Runnable
zurückgeben.
Werte prüfen und ändern
Die Eigenschaften eines Optional
-Objektes lassen sich
mit der Methode filter()
prüfen. Es erwartet ein
Predicate
Functional Interface als Parameter und
liefert seinerseits wiederum ein Optional
, welches
entweder dem Wert des Predicate
entspricht oder leer
ist. Mit isPresent()
kann dies dann nachgeprüft
werden.
Optional<String> os = Optional.of("Hallo Welt!"); boolean b = os.filter(s -> s.equals("Hallo Welt!")).isPresent(); System.out.println(b); // true
Möchte man den Wert eines Optional
bearbeiten, so
können dazu die Methoden map()
und flatMap()
verwendet werden.
Optional<String> os = Optional.of("Hallo Welt!"); String s = os.map(String::toUpperCase).orElse("String leer"); System.out.println(s); // HALLO WELT!
Im Gegensatz zu map()
verwendet flatMap()
als Parameter wiederum einen gekapselten Wert in Form eines Optional
.
class Moped { Motor motor = new Motor(); //... public Optional<Motor> getMotor() { return Optional.ofNullable(motor); } } class Motor { int hubraum = 1100; public Optional<Integer> getHubraum() { return Optional.ofNullable(hubraum); } } //... int hubraum = moped.flatMap(Moped::getMotor).flatMap(Motor::getHubraum).orElse(0); System.out.println(hubraum); // 1100
Quellen
https://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.