Was ist ein Optional? v.8

Mit der Version 8 wird die Klasse java.util.Optional<T> eingeführt und mit Java 9 ergänzt. Sie stellt Methoden bereit, um den oft lästigen und aufgeblähten Code produzierenden Umgang mit null-Werten deutlich zu vereinfachen.

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