Suche
Für das Plus an Optionals

Java 9: Erweiterungen der Klasse „Optional<T>“

Michael Inden

© Shutterstock.com / Profit_Image

Das kürzlich erschienene Java 9 besteht aus mehr als nur Modulen und dem Projekt Jigsaw. Über 90 sogenannte JEPs (JDK Enhancement Proposals) sind Teil von Version 9 des Java Development Kits. Im vierten Teil seiner Serie zu Java 9 stellt Michael Inden, Oracle-zertifizierter Java-Entwickler, einige mit JDK 9 eingeführte Erweiterungen der Klasse Optional<T> vor, die nicht Teil eines JEPs sind.

Die vorliegende Artikelserie zu Java 9 setzt sich aus Texten von Michael Indens Buch „Java 9 – Die Neuerungen“ zusammen, das beim dpunkt.verlag erschienen ist.

PS: Verpassen Sie nicht unsere Infografik zu Project Jigsaw!

Nachdem wir im ersten Teil dieser Blog-Serie diverse kleinere Änderungen in der Syntax der Sprache Java kennengelernt haben, schauten wir uns im zweiten Teil einige relevante Erweiterungen im JDK an, konkreter: im Process-API und den Collection-Factory-Methoden. Im dritten Teil ging es anschließend um die Ergänzungen im Stream-API, im vorliegenden vierten Teil bespricht Michael Inden die Erweiterungen in Optional<T>.

Erweiterungen in „Optional<T>“

Die Klasse java.util.Optional<T> wurde mit Java 8 eingeführt und erleichtert die Behandlung und Modellierung optionaler Werte. Im Praxiseinsatz der Klasse Optional<T> war jedoch bislang noch die eine oder andere Schwachstelle festzustellen. Insbesondere betrifft dies folgende Aufgabenstellungen:

  1. Das Ausführen von Aktionen auch im Negativfall.
  2. Die Umwandlung in einen Stream<T>, um Daten weiterzuverarbeiten oder eine Kompatibilität mit dem Stream-API z. B. für Frameworks, die auf Streams arbeiten, herzustellen.
  3. Die Verknüpfung der Resultate mehrerer Berechnungen, die Optional<T> liefern.

Durch die Erweiterungen der Klasse Optional<T> in JDK 9 wurden alle der drei zuvor aufgelisteten Schwachstellen adressiert. Dazu dienen folgende Methoden:

  • ifPresentOrElse(Consumer<? super T>, Runnable) – Erlaubt die Ausführung einer Aktion im Positiv- oder im Negativfall.
  • stream() – Wandelt das Optional<T> in einen Stream<T> um.
  • or(Supplier<Optional<T>>> supplier) – Ermöglicht auf eleganteWeise die Verknüpfung mehrerer Berechnungen.

Die Methode „ifPresentOrElse()“

Durch Einsatz dieser Methode lässt sich die Ergebnisauswertung von Suchen oftmals vereinfachen, nachfolgend am Beispiel von Tim und einem ungültigen Kunden UNKNOWN gezeigt:

public static void main(final String[] args)
{
    final Optional<String> optCustomer1 = findCustomer("Tim");
    optCustomer1.ifPresentOrElse(str -> System.out.println("found: " + str),
                                 () -> System.out.println("not found"));

    final Optional<String> optCustomer2 = findCustomer("UNKNOWN");
    optCustomer2.ifPresentOrElse(str -> System.out.println("found: " + str),
                                  () -> System.out.println("not found"));
}

Startet man das obige Programm, so kommt es zu den folgenden Ausgaben, wenn der Kunde Tim existiert:

findCustomer(Tim)
found: Tim
findCustomer(UNKNWON)
not found

Die Methode „stream()“

Manchmal ist es praktisch, ein Optional<T> in einen Stream<T> umzuwandeln. Das wird nun durch die Methode stream() möglich. Deren Nutzen kann man sich gut für einen Stream von optionalen Werten verdeutlichen, falls dort nur diejenigen Einträge mit gültigen Werten behalten werden sollen. Das kann man durch die Kombination der Methoden flatMap() und stream() erreichen. Hierbei wird jedes Optional<T> in einen Stream überführt und durch flatMap() zu einem einzigen Stream mit Werten kombiniert. Das Verfahren zeige ich am Beispiel eines Streams, der aus Optional<String>-Elementen besteht, beispielsweise als Folge einer parallelen Suche. Am Ende sollen die Ergebnisse konsolidiert werden. Diese Anforderung realisieren wir wie folgt:

public static void main(final String[] args)
{
    final Stream<Optional<String>> streamOfOptionalNames = Stream.of(
                                   Optional.of("Tim"), Optional.of("Tom"),
                                   Optional.empty(), Optional.of("Mike"),
                                   Optional.empty(), Optional.of("Andy"));

    final Stream<String> streamOfNames =
                         streamOfOptionalNames.flatMap(Optional::stream);

    streamOfNames.forEach(value -> System.out.println("found: " + value));
}

Die Methode „or()“

Nach den beiden grundlegenden Erweiterungen schauen wir abschließend auf die so unscheinbar wirkende Methode or(Supplier<? extends Optional<? extends T>>). Mit deren Hilfe lassen sich Methoden bzw. Aufrufketten mit Fallback-Strategien auf lesbare und verständliche Art beschreiben, wie es die Methode multiFindCustomer(String) eindrucksvoll zeigt.

public static void main(final String[] args)
{
    final Optional<String> optCustomer = multiFindCustomer("Tim");
    optCustomer.ifPresentOrElse(str -> System.out.println("found: " + str),
                                () -> System.out.println("not found"));
}

private static Optional<String> multiFindCustomer(final String customerId)
{
    return findInCache(customerId)
           .or(() -> findInMemory(customerId))
           .or(() -> findInDb(customerId));
}

(Bei der Beschreibung für die Methode or() habe ich mich von einem Beispiel vom CodeFX-Blog inspirieren lassen.)

Zwei der aufgerufenen Suchmethoden sind bewusst sehr simpel und kurz realisiert und liefern lediglich Optional.EMPTY zurück. Damit ergibt sich folgende Realisierung für findInCache(String) und findInDb(String):

private static Optional<String> findInCache(final String customerId)
{
    System.out.println("findInCache");
    return Optional.empty();
}

private static Optional<String> findInDb(final String customerId)
{
    System.out.println("findInDb");
    return Optional.empty();
}

In der Methode multiFindCustomer(String) werden nacheinander drei Abfragen ausgeführt, sofern nicht zuvor ein Treffer gefunden wird. Das ist hier für den zweiten Aufruf findInMemory(String) der Fall, weil diese Methode eine Suche in einem Stream<String> wie folgt ausführt:

private static Optional<String> findInMemory(final String customerId)
{
    System.out.println("findInMemory");
    final Stream<String> customers = Stream.of("Tim", "Tom", "Mike", "Andy");

    return customers.filter(name -> name.contains(customerId))
                    .findFirst();
}

Startet man das obige Programm, so kommt es zu folgenden Ausgaben, die die Arbeitsweise verdeutlichen:

findInCache
findInMemory
Tim

Zunächst wird nach Tim im Cache durch findInCache(String) gesucht, jedoch ohne Erfolg. Das zurückgelieferte Optional.EMPTY führt automatisch dazu, dass die zweite Methode in der Aufrufkette, also hier findInMemory(String), ausgeführt wird. Weil die Suche nach Tim dort erfolgreich ist, bricht die Verarbeitung ab und es wird das Suchergebnis als Optional<String> zurückgegeben.

Java 9 – Die Neuerungen

Michael Indens Buch bietet einen fundierten Einstieg in Java 9 sowie einen Überblick über die umfangreichen Neuerungen in der aktuellen Version. Damit eignet sich das Buch für all jene, die ihr Java-Wissen aktualisieren wollen. Dabei helfen eine Vielzahl an Übungen, um die einzelnen Themengebiete zu vertiefen und besser zu verstehen.

Neben Änderungen an der Sprache bilden auch die Erweiterungen in diversen APIs einen Schwerpunkt. Fehlen darf hier natürlich auch nicht ein Kapitel über die fundamentalste Änderung in Java 9: Project Jigsaw. Auch fortgeschrittenere Themen wie Services und die Migration bestehender Applikationen werden besprochen. Da Java 9 auch einige Auswirkungen auf Build Tools und IDEs mit sich bringt, gibt ein Kapitel einen Überblick über den derzeitigen Stand zum Tooling rund um die neue Java-Version.

Weitere Informationenen zum Buch gibt es hier!

Verwandte Themen:

Geschrieben von
Michael Inden
Michael Inden
Dipl.-Inform. Michael Inden ist Oracle-zertifizierter Java-Entwickler für JDK 6. Nach seinem Studium in Oldenburg war er lange Zeit als Softwareentwickler und -architekt bei verschiedenen internationalen Firmen tätig und arbeitet derzeit als Teamleiter Softwareentwicklung in Zürich. Michael Inden hat rund 20 Jahre Erfahrung beim Entwurf komplexer Softwaresysteme gesammelt, an diversen Fortbildungen und an mehreren Java-One-Konferenzen in San Francisco teilgenommen. Sein Wissen gibt er gerne als Trainer in Schulungen und auf Konferenzen weiter. Sein besonderes Interesse gilt dem Design qualitativ hochwertiger Applikationen mit ergonomischen, grafischen Oberflächen sowie dem Coaching von Kollegen.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.