Anleitung zu Java 8 Optional

Anleitung zu Java 8 Optional

1. Überblick

In diesem Tutorial zeigen wir die in Java 8 eingeführteOptional-Klasse.

Der Zweck der Klasse besteht darin, eine Lösung auf Typebene für die Darstellung optionaler Werte anstelle vonnull-Referenzen bereitzustellen.

Um ein tieferes Verständnis dafür zu bekommen, warum wir uns für die KlasseOptionalinteressieren sollten, werfen Sie einen Blick aufthe official Oracle’s article.

Weitere Lektüre:

Java Optional als Rückgabetyp

Erfahren Sie, welche Best Practices es gibt und wann Sie den optionalen Typ in Java zurückgeben müssen.

Read more

Java 9 Optionale API-Ergänzungen

Schnelle und praktische Beispiele für neue Methoden in der optionalen API in Java.

Read more

2. Optional Objekte erstellen

Es gibt verschiedene Möglichkeiten,Optional-Objekte zu erstellen. Um ein leeresOptional-Objekt zu erstellen, müssen wir lediglich die statische Methode vonemptyverwenden:

@Test
public void whenCreatesEmptyOptional_thenCorrect() {
    Optional empty = Optional.empty();
    assertFalse(empty.isPresent());
}

Beachten Sie, dass wir die MethodeisPresent() verwendet haben, um zu überprüfen, ob das ObjektOptionaleinen Wert enthält. Ein Wert ist nur vorhanden, wenn wirOptional mit einem Wert von nichtnull erstellt haben. Wir werden uns im nächsten Abschnitt dieisPresent-Methode ansehen.

Wir können auch einOptional-Objekt mit der statischen Methodeof erstellen:

@Test
public void givenNonNull_whenCreatesNonNullable_thenCorrect() {
    String name = "example";
    Optional opt = Optional.of(name);
    assertTrue(opt.isPresent());
}

Das an dieof()-Methode übergebene Argument kann jedoch nichtnull. sein. Andernfalls erhalten wir einNullPointerException:

@Test(expected = NullPointerException.class)
public void givenNull_whenThrowsErrorOnCreate_thenCorrect() {
    String name = null;
    Optional.of(name);
}

Falls wir jedoch einigenull-Werte erwarten, können wir dieofNullable()-Methode verwenden:

@Test
public void givenNonNull_whenCreatesNullable_thenCorrect() {
    String name = "example";
    Optional opt = Optional.ofNullable(name);
    assertTrue(optionalName.isPresent());
}

Wenn wir auf diese Weise einenull-Referenz übergeben, wird keine Ausnahme ausgelöst, sondern ein leeresOptional-Objekt zurückgegeben:

@Test
public void givenNull_whenCreatesNullable_thenCorrect() {
    String name = null;
    Optional opt = Optional.ofNullable(name);
    assertFalse(optionalName.isPresent());
}

3. Überprüfen der Wertpräsenz:isPresent() undisEmpty()

Wenn einOptional-Objekt von einer Methode zurückgegeben oder von uns erstellt wurde, können wir mit derisPresent()-Methode prüfen, ob ein Wert darin enthalten ist oder nicht:

@Test
public void givenOptional_whenIsPresentWorks_thenCorrect() {
    Optional opt = Optional.of("example");
    assertTrue(opt.isPresent());

    opt = Optional.ofNullable(null);
    assertFalse(opt.isPresent());
}

Diese Methode gibttrue zurück, wenn der umschlossene Wert nichtnull. ist

Ab Java 11 können wir mit derisEmpty -Smethod auch das Gegenteil tun:

@Test
public void givenAnEmptyOptional_thenIsEmptyBehavesAsExpected() {
    Optional opt = Optional.of("example");
    assertFalse(opt.isEmpty());

    opt = Optional.ofNullable(null);
    assertTrue(opt.isEmpty());
}

4. Bedingte Aktion MitifPresent()

Mit der MethodeifPresent() können wir Code für den umschlossenen Wert ausführen, wenn festgestellt wird, dass er nicht -null ist. VorOptional würden wir Folgendes tun:

if(name != null) {
    System.out.println(name.length());
}

Dieser Code prüft, ob die Namensvariablenull ist oder nicht, bevor er Code darauf ausführt. Dieser Ansatz ist langwierig und nicht nur das Problem, sondern auch fehleranfällig.

Was garantiert uns, dass wir diese Variable nach dem Drucken nicht mehr verwenden und dannforget to perform the null check.

This can result in a NullPointerException at runtime if a null value finds its way into that code. Wenn ein Programm aufgrund von Eingabeproblemen fehlschlägt, ist dies häufig auf schlechte Programmierpraktiken zurückzuführen.

Optional lässt uns explizit mit nullbaren Werten umgehen, um eine gute Programmierpraxis durchzusetzen. Schauen wir uns nun an, wie der obige Code in Java 8 überarbeitet werden könnte.

In einem typischen funktionalen Programmierstil können wir eine Aktion für ein Objekt ausführen, das tatsächlich vorhanden ist:

@Test
public void givenOptional_whenIfPresentWorks_thenCorrect() {
    Optional opt = Optional.of("example");
    opt.ifPresent(name -> System.out.println(name.length()));
}

Im obigen Beispiel verwenden wir nur zwei Codezeilen, um die fünf zu ersetzen, die im ersten Beispiel funktioniert haben. Eine Zeile zum Umschließen des Objekts in einOptional-Objekt und die nächste zum Durchführen einer impliziten Validierung sowie zum Ausführen des Codes.

5. Standardwert MitorElse()

DieorElse()-Methode wird verwendet, um den Wert abzurufen, der in einerOptional-Instanz eingeschlossen ist. Es wird ein Parameter als Standardwert verwendet. Die MethodeorElse()gibt den umschlossenen Wert zurück, wenn er vorhanden ist, und das Argument anders:

@Test
public void whenOrElseWorks_thenCorrect() {
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElse("john");
    assertEquals("john", name);
}

6. Standardwert MitorElseGet()

Die MethodeorElseGet() ähneltorElse(). Anstatt jedoch einen Wert für die Rückgabe zu verwenden, wenn der Wert vonOptionalnicht vorhanden ist, wird eine aufgerufene Lieferantenfunktionsschnittstelle verwendet, die aufgerufen wird und den Wert des Aufrufs zurückgibt:

@Test
public void whenOrElseGetWorks_thenCorrect() {
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElseGet(() -> "john");
    assertEquals("john", name);
}

7. Differenz zwischenorElse undorElseGet()

Für viele Programmierer, dieOptional oder Java 8 noch nicht kennen, ist der Unterschied zwischenorElse() undorElseGet() nicht klar. Tatsächlich erwecken diese beiden Methoden den Eindruck, dass sie sich in ihrer Funktionalität überlappen.

Es gibt jedoch einen subtilen, aber sehr wichtigen Unterschied zwischen den beiden, der die Leistung unseres Codes drastisch beeinträchtigen kann, wenn er nicht gut verstanden wird.

Erstellen wir in der Testklasse eine Methode namensgetMyDefault(), die keine Argumente akzeptiert und einen Standardwert zurückgibt:

public String getMyDefault() {
    System.out.println("Getting Default Value");
    return "Default Value";
}

Sehen wir uns zwei Tests an und beobachten ihre Nebenwirkungen, um festzustellen, wo sichorElse() undorElseGet() überlappen und wo sie sich unterscheiden:

@Test
public void whenOrElseGetAndOrElseOverlap_thenCorrect() {
    String text = null;

    String defaultText = Optional.ofNullable(text).orElseGet(this::getMyDefault);
    assertEquals("Default Value", defaultText);

    defaultText = Optional.ofNullable(text).orElse(getMyDefault());
    assertEquals("Default Value", defaultText);
}

Im obigen Beispiel wird ein Nulltext in einOptional-Objekt eingeschlossen und versucht, den umgebrochenen Wert mit jedem der beiden Ansätze abzurufen. Die Nebenwirkung ist wie folgt:

Getting default value...
Getting default value...

Die MethodegetMyDefault() wird jeweils aufgerufen. Es kommt also vor, dasswhen the wrapped value is not present, then both orElse() and orElseGet() work exactly the same way.

Führen Sie nun einen weiteren Test durch, bei dem der Wert vorhanden ist. Im Idealfall sollte der Standardwert nicht einmal erstellt werden:

@Test
public void whenOrElseGetAndOrElseDiffer_thenCorrect() {
    String text = "Text present";

    System.out.println("Using orElseGet:");
    String defaultText
      = Optional.ofNullable(text).orElseGet(this::getMyDefault);
    assertEquals("Text present", defaultText);

    System.out.println("Using orElse:");
    defaultText = Optional.ofNullable(text).orElse(getMyDefault());
    assertEquals("Text present", defaultText);
}

Im obigen Beispiel wird einnull-Wert nicht mehr umbrochen, und der Rest des Codes bleibt gleich. Schauen wir uns nun den Nebeneffekt der Ausführung dieses Codes an:

Using orElseGet:
Using orElse:
Getting default value...

Beachten Sie, dass bei Verwendung vonorElseGet() zum Abrufen des umschlossenen Werts die MethodegetMyDefault() nicht einmal aufgerufen wird, da der enthaltene Wert vorhanden ist.

Bei Verwendung vonorElse() wird jedoch das Standardobjekt erstellt, unabhängig davon, ob der umschlossene Wert vorhanden ist oder nicht. In diesem Fall haben wir nur ein redundantes Objekt erstellt, das niemals verwendet wird.

In diesem einfachen Beispiel ist die Erstellung eines Standardobjekts ohne erhebliche Kosten verbunden, da die JVM weiß, wie mit solchen umzugehen ist. However, when a method such as getMyDefault() has to make a web service call or even query a database, then the cost becomes very obvious.

8. Ausnahmen mitorElseThrow()

Die MethodeorElseThrow() folgt ausorElse() undorElseGet() und fügt einen neuen Ansatz für die Behandlung eines fehlenden Werts hinzu. Anstatt einen Standardwert zurückzugeben, wenn der umgebrochene Wert nicht vorhanden ist, wird eine Ausnahme ausgelöst:

@Test(expected = IllegalArgumentException.class)
public void whenOrElseThrowWorks_thenCorrect() {
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElseThrow(
      IllegalArgumentException::new);
}

Hier bieten sich Methodenreferenzen in Java 8 an, die im Exception-Konstruktor übergeben werden.

9. Rückgabewert mitget()

Der letzte Ansatz zum Abrufen des umschlossenen Werts ist dieget()-Methode:

@Test
public void givenOptional_whenGetsValue_thenCorrect() {
    Optional opt = Optional.of("example");
    String name = opt.get();
    assertEquals("example", name);
}

Im Gegensatz zu den obigen drei Ansätzen kannget() jedoch nur dann einen Wert zurückgeben, wenn das umschlossene Objekt nichtnull ist. Andernfalls wird eine Ausnahme für ein solches Element ausgelöst:

@Test(expected = NoSuchElementException.class)
public void givenOptionalWithNull_whenGetThrowsException_thenCorrect() {
    Optional opt = Optional.ofNullable(null);
    String name = opt.get();
}

Dies ist der Hauptfehler derget()-Methode. Im Idealfall solltenOptional uns helfen, solche unvorhergesehenen Ausnahmen zu vermeiden. Daher arbeitet dieser Ansatz gegen die Ziele vonOptional und wird wahrscheinlich in einer zukünftigen Version veraltet sein.

Es ist daher ratsam, die anderen Varianten zu verwenden, die es uns ermöglichen, den Fallnullvorzubereiten und explizit zu behandeln.

10. Bedingte Rückgabe mitfilter()

Mit der Methodefilterkönnen wir einen Inline-Test für unseren umschlossenen Wert durchführen. Es nimmt ein Prädikat als Argument und gibt das ObjektOptionalzurück. Wenn der umschlossene Wert die Prüfung durch das Prädikat besteht, wirdOptional unverändert zurückgegeben.

Wenn das Prädikat jedochfalse zurückgibt, gibt es ein leeresOptional zurück:

@Test
public void whenOptionalFilterWorks_thenCorrect() {
    Integer year = 2016;
    Optional yearOptional = Optional.of(year);
    boolean is2016 = yearOptional.filter(y -> y == 2016).isPresent();
    assertTrue(is2016);
    boolean is2017 = yearOptional.filter(y -> y == 2017).isPresent();
    assertFalse(is2017);
}

Diefilter-Methode wird normalerweise auf diese Weise verwendet, um umschlossene Werte basierend auf einer vordefinierten Regel abzulehnen. Wir könnten es verwenden, um ein falsches E-Mail-Format oder ein Passwort abzulehnen, das nicht stark genug ist.

Schauen wir uns ein weiteres aussagekräftiges Beispiel an. Nehmen wir an, wir möchten ein Modem kaufen und kümmern uns nur um dessen Preis. Wir erhalten Push-Benachrichtigungen zu Modempreisen von einer bestimmten Site und speichern diese in Objekten:

public class Modem {
    private Double price;

    public Modem(Double price) {
        this.price = price;
    }
    // standard getters and setters
}

Wir geben diese Objekte dann an einen Code weiter, dessen einziger Zweck darin besteht, zu überprüfen, ob der Preis für das Modem in unserem Budgetbereich liegt.

Schauen wir uns nun den Code ohneOptional an:

public boolean priceIsInRange1(Modem modem) {
    boolean isInRange = false;

    if (modem != null && modem.getPrice() != null
      && (modem.getPrice() >= 10
        && modem.getPrice() <= 15)) {

        isInRange = true;
    }
    return isInRange;
}

Achten Sie darauf, wie viel Code wir schreiben müssen, um dies zu erreichen, insbesondere im Zustandif. Der einzige Teil der if-Bedingung, der für die Anwendung von entscheidender Bedeutung ist, ist die letzte Prüfung der Preisspanne. Die restlichen Schecks sind defensiv:

@Test
public void whenFiltersWithoutOptional_thenCorrect() {
    assertTrue(priceIsInRange1(new Modem(10.0)));
    assertFalse(priceIsInRange1(new Modem(9.9)));
    assertFalse(priceIsInRange1(new Modem(null)));
    assertFalse(priceIsInRange1(new Modem(15.5)));
    assertFalse(priceIsInRange1(null));
}

Abgesehen davon ist es möglich, die Nullprüfungen an einem langen Tag zu vergessen, ohne dass Fehler bei der Kompilierung auftreten.

Betrachten wir nun eine Variante mitOptional#filter:

public boolean priceIsInRange2(Modem modem2) {
     return Optional.ofNullable(modem2)
       .map(Modem::getPrice)
       .filter(p -> p >= 10)
       .filter(p -> p <= 15)
       .isPresent();
 }

The map call is simply used to transform a value to some other value. Beachten Sie, dass durch diesen Vorgang der ursprüngliche Wert nicht geändert wird.

In unserem Fall erhalten wir ein Preisobjekt aus der KlasseModel. Wir werden uns die Methodemap()im nächsten Abschnitt genauer ansehen.

Wenn einnull-Objekt an diese Methode übergeben wird, erwarten wir zunächst kein Problem.

Zweitens ist die einzige Logik, die wir in ihren Körper schreiben, genau das, was der Methodenname beschreibt, die Preisspannenprüfung. Optional kümmert sich um den Rest:

@Test
public void whenFiltersWithOptional_thenCorrect() {
    assertTrue(priceIsInRange2(new Modem(10.0)));
    assertFalse(priceIsInRange2(new Modem(9.9)));
    assertFalse(priceIsInRange2(new Modem(null)));
    assertFalse(priceIsInRange2(new Modem(15.5)));
    assertFalse(priceIsInRange2(null));
}

Der bisherige Ansatz verspricht, die Preisspanne zu überprüfen, muss aber mehr als das tun, um seine inhärente Zerbrechlichkeit zu verteidigen. Daher können wir diefilter-Methode verwenden, um unnötigeif-Anweisungen zu ersetzen und unerwünschte Werte abzulehnen.

11. Wert mitmap() transformieren

Im vorherigen Abschnitt haben wir uns angesehen, wie ein Wert basierend auf einem Filter abgelehnt oder akzeptiert wird. Wir können eine ähnliche Syntax verwenden, um den Wert vonOptionalmit der Methodemap()zu transformieren:

@Test
public void givenOptional_whenMapWorks_thenCorrect() {
    List companyNames = Arrays.asList(
      "paypal", "oracle", "", "microsoft", "", "apple");
    Optional> listOptional = Optional.of(companyNames);

    int size = listOptional
      .map(List::size)
      .orElse(0);
    assertEquals(6, size);
}

In diesem Beispiel wickeln wir eine Liste von Zeichenfolgen in einOptional-Objekt ein und verwenden diemap-Methode, um eine Aktion für die enthaltene Liste auszuführen. Die Aktion, die wir ausführen, besteht darin, die Größe der Liste abzurufen.

Die Methodemap gibt das Ergebnis der Berechnung zurück, die inOptional eingeschlossen ist. Wir müssen dann eine geeignete Methode für die zurückgegebenenOptional aufrufen, um ihren Wert abzurufen.

Beachten Sie, dass die Methodefilter einfach eine Überprüfung des Werts durchführt undboolean zurückgibt. Andererseits nimmt diemap-Methode den vorhandenen Wert, führt eine Berechnung mit diesem Wert durch und gibt das Ergebnis der Berechnung zurück, die in einOptional-Objekt eingeschlossen ist:

@Test
public void givenOptional_whenMapWorks_thenCorrect2() {
    String name = "example";
    Optional nameOptional = Optional.of(name);

    int len = nameOptional
     .map(String::length)
     .orElse(0);
    assertEquals(8, len);
}

Wir könnenmap undfilter miteinander verketten, um etwas Stärkeres zu tun.

Nehmen wir an, wir möchten die Richtigkeit einer von einem Benutzer eingegebenen Kennwort überprüfen. Wir können das Passwort mit einermap-Transformation bereinigen und die Richtigkeit mit einemfilter überprüfen:

@Test
public void givenOptional_whenMapWorksWithFilter_thenCorrect() {
    String password = " password ";
    Optional passOpt = Optional.of(password);
    boolean correctPassword = passOpt.filter(
      pass -> pass.equals("password")).isPresent();
    assertFalse(correctPassword);

    correctPassword = passOpt
      .map(String::trim)
      .filter(pass -> pass.equals("password"))
      .isPresent();
    assertTrue(correctPassword);
}

Wie wir sehen können, wird die Eingabe ohne vorherige Bereinigung herausgefiltert - dennoch können Benutzer davon ausgehen, dass führende und nachfolgende Leerzeichen alle Eingaben darstellen. Also wandeln wir ein schmutziges Passwort in ein sauberes mitmap um, bevor wir falsche herausfiltern.

12. Wert mitflatMap() transformieren

Genau wie diemap()-Methode haben wir auch dieflatMap()-Methode als Alternative zur Transformation von Werten. Der Unterschied besteht darin, dassmap Werte nur transformiert, wenn sie entpackt werden, währendflatMap einen umschlossenen Wert nimmt und ihn vor der Transformation entpackt.

Zuvor haben wir einfacheString- undInteger-Objekte zum Umschließen in eineOptional-Instanz erstellt. Häufig erhalten wir diese Objekte jedoch von einem Zugriffsmechanismus für ein komplexes Objekt.

Um ein klareres Bild des Unterschieds zu erhalten, schauen wir uns einPerson-Objekt an, das die Details einer Person wie Name, Alter und Passwort enthält:

public class Person {
    private String name;
    private int age;
    private String password;

    public Optional getName() {
        return Optional.ofNullable(name);
    }

    public Optional getAge() {
        return Optional.ofNullable(age);
    }

    public Optional getPassword() {
        return Optional.ofNullable(password);
    }

    // normal constructors and setters
}

Normalerweise würden wir ein solches Objekt erstellen und es in einOptional-Objekt einschließen, genau wie wir es mit String getan haben. Alternativ kann es durch einen anderen Methodenaufruf an uns zurückgegeben werden:

Person person = new Person("john", 26);
Optional personOptional = Optional.of(person);

Beachten Sie jetzt, dass beim Umschließen einesPerson-Objekts verschachtelteOptional-Instanzen enthalten sind:

@Test
public void givenOptional_whenFlatMapWorks_thenCorrect2() {
    Person person = new Person("john", 26);
    Optional personOptional = Optional.of(person);

    Optional> nameOptionalWrapper
      = personOptional.map(Person::getName);
    Optional nameOptional
      = nameOptionalWrapper.orElseThrow(IllegalArgumentException::new);
    String name1 = nameOptional.orElse("");
    assertEquals("john", name1);

    String name = personOptional
      .flatMap(Person::getName)
      .orElse("");
    assertEquals("john", name);
}

Hier versuchen wir, das Namensattribut desPerson-Objekts abzurufen, um eine Zusicherung durchzuführen.

Beachten Sie in der dritten Anweisung, wie wir dies mit der Methodemap()erreichen, und beachten Sie anschließend, wie wir dies anschließend mit der MethodeflatMap()tun.

Die Methodenreferenz vonPerson::getNameähnelt dem Aufruf vonString::trim, den wir im vorherigen Abschnitt zum Bereinigen eines Kennworts hatten.

Der einzige Unterschied besteht darin, dassgetName() einOptional anstelle eines Strings zurückgibt, wie dies bei der Operationtrim()der Fall war. Dies führt zusammen mit der Tatsache, dass einemap-Transformation das Ergebnis in einOptional-Objekt einschließt, zu einem verschachteltenOptional.

Bei Verwendung der Methodemap()müssen wir daher einen zusätzlichen Aufruf hinzufügen, um den Wert abzurufen, bevor der transformierte Wert verwendet wird. Auf diese Weise wird der WrapperOptionalentfernt. Diese Operation wird implizit ausgeführt, wennflatMap verwendet wird.

13. Verkettung vonOptionals in Java 8

Manchmal müssen wir möglicherweise das erste nicht leereOptional-Objekt aus einer Anzahl vonOptionals abrufen. In solchen Fällen wäre es sehr praktisch, eine Methode wieorElseOptional() zu verwenden. Leider wird eine solche Operation in Java 8 nicht direkt unterstützt.

Lassen Sie uns zunächst einige Methoden vorstellen, die wir in diesem Abschnitt verwenden werden:

private Optional getEmpty() {
    return Optional.empty();
}

private Optional getHello() {
    return Optional.of("hello");
}

private Optional getBye() {
    return Optional.of("bye");
}

private Optional createOptional(String input) {
    if (input == null || "".equals(input) || "empty".equals(input)) {
        return Optional.empty();
    }
    return Optional.of(input);
}

Um mehrereOptional Objekte zu verketten und das erste nicht leere Objekt in Java 8 zu erhalten, können wir dieStream API verwenden:

@Test
public void givenThreeOptionals_whenChaining_thenFirstNonEmptyIsReturned() {
    Optional found = Stream.of(getEmpty(), getHello(), getBye())
      .filter(Optional::isPresent)
      .map(Optional::get)
      .findFirst();

    assertEquals(getHello(), found);
}

Der Nachteil dieses Ansatzes ist, dass alle unsereget-Methoden immer ausgeführt werden, unabhängig davon, wo inStream ein nicht leeresOptional erscheint.

Wenn wir die anStream.of() übergebenen Methoden träge auswerten möchten, müssen wir die Methodenreferenz und dieSupplier-Schnittstelle verwenden:

@Test
public void givenThreeOptionals_whenChaining_thenFirstNonEmptyIsReturnedAndRestNotEvaluated() {
    Optional found =
      Stream.>>of(this::getEmpty, this::getHello, this::getBye)
        .map(Supplier::get)
        .filter(Optional::isPresent)
        .map(Optional::get)
        .findFirst();

    assertEquals(getHello(), found);
}

Wenn wir Methoden verwenden müssen, die Argumente annehmen, müssen wir auf Lambda-Ausdrücke zurückgreifen:

@Test
public void givenTwoOptionalsReturnedByOneArgMethod_whenChaining_thenFirstNonEmptyIsReturned() {
    Optional found = Stream.>>of(
      () -> createOptional("empty"),
      () -> createOptional("hello")
    )
      .map(Supplier::get)
      .filter(Optional::isPresent)
      .map(Optional::get)
      .findFirst();

    assertEquals(createOptional("hello"), found);
}

Oft möchten wir einen Standardwert zurückgeben, falls alle verkettetenOptionals leer sind. Wir können dies tun, indem wirorElse() oderorElseGet() wie im folgenden Beispiel aufrufen:

@Test
public void givenTwoEmptyOptionals_whenChaining_thenDefaultIsReturned() {
    String found = Stream.>>of(
      () -> createOptional("empty"),
      () -> createOptional("empty")
    )
      .map(Supplier::get)
      .filter(Optional::isPresent)
      .map(Optional::get)
      .findFirst()
      .orElseGet(() -> "default");

    assertEquals("default", found);
}

14. JDK 9Optional API

Mit der Veröffentlichung von Java 9 wurden derOptional-API noch weitere neue Methoden hinzugefügt:

  • die Methodeor() zur Bereitstellung eines Lieferanten, der eine AlternativeOptional erstellt

  • dieifPresentOrElse()-Methode, mit der eine Aktion ausgeführt werden kann, wennOptional vorhanden ist, oder eine andere Aktion, wenn nicht

  • stream() Methode zur Umwandlung vonOptional inStream

Hier ist der vollständige Artikel fürfurther reading.

15. Missbrauch vonOptionals

Lassen Sie uns abschließend eine verlockende, jedoch gefährliche Methode zur Verwendung vonOptionals sehen: Übergeben einesOptional-Parameters an eine Methode.

Stellen Sie sich vor, wir haben eine Liste mitPerson und möchten, dass eine Methode diese Liste nach Personen mit einem bestimmten Namen durchsucht. Außerdem möchten wir, dass diese Methode Einträge mit mindestens einem bestimmten Alter vergleicht, sofern dies angegeben ist. Da dieser Parameter optional ist, haben wir folgende Methode:

public List search(List people, String name, Optional age) {
    // Null checks for people and name
    people.stream()
      .filter(p -> p.getName().equals(name))
      .filter(p -> p.getAge() >= age.orElse(0))
      .collect(Collectors.toList());
}

Dann veröffentlichen wir unsere Methode und ein anderer Entwickler versucht, sie zu verwenden:

someObject.search(people, "Peter", null);

Jetzt führt der Entwickler seinen Code aus und erhältNullPointerException.There we are, having to null check our optional parameter, which defeats our initial purpose in wanting to avoid this kind of situation.

Hier sind einige Möglichkeiten, die wir hätten tun können, um besser damit umzugehen:

public List search(List people, String name, Integer age) {
    // Null checks for people and name

    age = age != null ? age : 0;
    people.stream()
      .filter(p -> p.getName().equals(name))
      .filter(p -> p.getAge() >= age)
      .collect(Collectors.toList());
}

Dort ist der Parameter noch optional, aber wir behandeln ihn nur in einer Prüfung. Eine andere Möglichkeit wärecreate two overloaded methods gewesen:

public List search(List people, String name) {
    return doSearch(people, name, 0);
}

public List search(List people, String name, int age) {
    return doSearch(people, name, age);
}

private List doSearch(List people, String name, int age) {
    // Null checks for people and name
    return people.stream()
      .filter(p -> p.getName().equals(name))
      .filter(p -> p.getAge() >= age)
      .collect(Collectors.toList());
}

Auf diese Weise bieten wir eine übersichtliche API mit zwei Methoden, die unterschiedliche Aufgaben ausführen (obwohl sie sich die Implementierung teilen).

Es gibt also Lösungen, um die Verwendung vonOptionals als Methodenparameter zu vermeiden. The intent of Java when releasing Optional was to use it as a return type, was darauf hinweist, dass eine Methode einen leeren Wert zurückgeben könnte. Tatsächlich beträgt die Praxis,Optional als Methodenparameter zu verwenden, sogardiscouraged by some code inspectors.

16. Fazit

In diesem Artikel haben wir die meisten wichtigen Funktionen der Java 8Optional-Klasse behandelt.

Wir haben auch kurz einige Gründe untersucht, warum wirOptional anstelle der expliziten Nullprüfung und Eingabevalidierung verwenden würden.

Wir haben auch gelernt, wie man mit den Methodenget(),orElse() undorElseGet() (undthe important difference between the two last) den Wert einesOptional oder eines Standardwerts (wenn leer) ermittelt ).

Dann haben wir gesehen, wie wir unsereOptionals mitmap(), flatMap() undfilter() transformieren oder filtern können.

Wir haben gesehen, was ein fließendesAPIOptional bietet, da es uns ermöglicht, die verschiedenen Methoden einfach zu verketten.

Schließlich haben wir gesehen, wie schlecht es ist,Optionals als Methodenparameter zu verwenden und wie man dies vermeidet.

Der Quellcode für alle Beispiele im Artikel istover on GitHub. verfügbar