2015-09-25 5 views
22

Hier ist, was ich bisher habe:Was ist die eleganteste Art, Optionale zu kombinieren?

Optional<Foo> firstChoice = firstChoice(); 
Optional<Foo> secondChoice = secondChoice(); 
return Optional.ofNullable(firstChoice.orElse(secondChoice.orElse(null))); 

Das erscheint mir als beide abscheulich und verschwenderisch. Wenn firstChoice vorhanden ist, erstelle ich unnötigerweise secondChoice.

Es gibt auch eine effizientere Version:

Optional<Foo> firstChoice = firstChoice(); 
if(firstChoice.isPresent()) { 
return firstChoice; 
} else { 
return secondChoice(); 
} 

Hier kann ich nicht Kette einige Mapping-Funktion bis zum Ende, ohne entweder den Mapper duplizieren oder eine andere lokale Variable deklarieren. All dies macht den Code komplizierter als das eigentliche Problem, das gelöst wird.

ich viel lieber würde dies schreiben:

return firstChoice().alternatively(secondChoice()); 

jedoch Optional :: alternativ existiert offensichtlich nicht. Was jetzt?

+4

möglich Duplikat [Get Wert von einer Optional oder einem anderen] (http://stackoverflow.com/questions/24599996/get-value- von der einen oder anderen Option – Tunaki

+0

... wenn wir mit dem Problem der Null konfrontiert werden ... jetzt haben wir zwei Probleme. – ZhongYu

+2

Btw, Paul Sandoz hat gerade ein [changeset] veröffentlicht (http://cr.openjdk.java.net/~psandoz/jdk9/JDK-8080418-optional-or/webrev/src/java.base/share/classes/java /util/Optional.java.udiff.html) für die Überprüfung, die die 'Optional.oder()' Methode einführt, die genau das tut, was Sie wollen: 'firstChoice(). or (this :: secondChoice)'. Weitere Informationen erhalten Sie unter [JDK-8080418] (https://bugs.openjdk.java.net/browse/JDK-8080418). In Java-9-mal werden wir glücklich sein! –

Antwort

29

Versuchen Sie folgendes:

firstChoice().map(Optional::of) 
      .orElseGet(this::secondChoice); 

Die Karte Methode gibt Ihnen einen Optional<Optional<Foo>>. Dann flacht die orElseGet Methode diese zurück zu Optional<Foo>. Die Methode secondChoice wird nur ausgewertet, wenn firstChoice() das leere optionale zurückgibt.

+13

Hinweis: Java 9 führt die 'Optional :: or' Methode ein. Dies könnte daher 'firstChoice(). Oder (this :: secondChoice)' in Java 9 geschrieben werden. – marstran

+0

Dies funktioniert nur mit Java8, wenn 'this :: secondChoice' 'Supplier' anstatt' Optional' zurückgibt - das Beispiel aus der OP nicht. – exception

+0

@exception 'this :: secondChoice' ist ein' Supplier'. Es ist eine Methodenreferenz. – marstran

5

können Sie einfach ersetzen, dass mit,

Optional<Foo> firstChoice = firstChoice(); 
return firstChoice.isPresent()? firstChoice : secondChoice(); 

Der obige Code wird nicht nennen, es sei denn firstChoice.isPresent() falsch ist.

Aber Sie müssen bereit sein, beide Funktionen aufzurufen, um die gewünschte Ausgabe zu erhalten. Es gibt keine andere Möglichkeit, der Überprüfung zu entgehen.

  • Der beste Fall ist die erste Wahl, die wahr zurückgibt.
  • Worst Case wird die erste Wahl false zurück, daher eine andere Methode Anruf für die zweite Wahl.
3

Vielleicht so etwas wie:

Optional<String> finalChoice = Optional.ofNullable(firstChoice() 
    .orElseGet(() -> secondChoice() 
    .orElseGet(() -> null))); 

Von: Chaining Optionals in Java 8

-1

Hier ist eine Art und Weise, die für beliebige Anzahl von Optional ‚s mit Sitz in einem Stream API funktioniert:

return Arrays.asList(firstChoice, secondChoice).stream() 
    .filter(Optional::isPresent) 
    .map(Optional::get) 
    .findFirst().orElse(null); 

Es ist nicht der kürzeste. Aber einfacher und verständlicher.

Ein anderer Weg ist firstNonNull() von Guava von commons-lang zu verwenden, wenn Sie bereits eine dieser Bibliotheken verwenden:

firstNonNull(firstChoice.orElse(null), secondChoice.orElse(null)); 
+0

Ich finde das viel weniger verständlich als die 'if' Lösung in der Frage, übrigens. – immibis

+0

Nun, ich stimme zu, wenn das einzige, was Sie haben, 2 'Optional's ist. Aus diesem Grund habe ich festgestellt, dass dieser Weg für eine beliebige Anzahl von 'Optional's funktioniert. –

+0

Das Problem Ihrer Lösung ist, dass es keinen Kurzschluss gibt: Sie müssen alle Optionen auswerten, was genau das ist, was das OP zu vermeiden versucht. In OPs sind der Code 'firstChoice()' und 'secondChoice()' eigentlich Methoden und Sie können Ihre Lösung nicht einfach so ändern, dass der Aufruf des zweiten nicht erfolgt, wenn das erste nicht-leere optionale erzeugt wird. –

3

Hier ist die Verallgemeinerung von @marstran Lösung für eine beliebige Anzahl von optionals:

@SafeVarargs 
public static <T> Optional<T> selectOptional(Supplier<Optional<T>>... optionals) { 
    return Arrays.stream(optionals) 
      .reduce((s1, s2) ->() -> s1.get().map(Optional::of).orElseGet(s2)) 
      .orElse(Optional::empty).get(); 
} 

Test:

public static Optional<String> first() { 
    System.out.println("foo called"); 
    return Optional.empty(); 
} 

public static Optional<String> second() { 
    System.out.println("bar called"); 
    return Optional.of("bar"); 
} 

public static Optional<String> third() { 
    System.out.println("baz called"); 
    return Optional.of("baz"); 
} 

public static void main(String[] args) { 
    System.out.println(selectOptional(() -> first(),() -> second(),() -> third())); 
} 

Ausgang:

foo called 
bar called 
Optional[bar] 
0

Ich wurde durch die Tatsache genug frustriert, dass dies nicht in Java 8 unterstützt wurde, dass ich wieder Guave des optionals geschaltet, die or haben:

public abstract Optional<T> or(Optional<? extends T> secondChoice) 

Rückgabe dieses Optional, wenn es hat ein Wert vorhanden; secondChoice sonst.

0

Faule Berechnungen und beliebige Anzahl von Optional Elementen

Stream.<Supplier<Optional<Foo>>>of(
     this::firstChoice, 
     this::secondChoice 
).map(
     Supplier::get 
).filter(
     Optional::isPresent 
).findFirst(
).orElseGet(
    Optional::empty 
); 
Verwandte Themen