it-swarm.com.de

Was ist die eleganteste Art, Optionen zu kombinieren?

Hier ist was ich bis jetzt habe:

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

Dies erscheint mir sowohl abscheulich als auch verschwenderisch. Wenn firstChoice anwesend ist, berechne ich secondChoice unnötig.

Es gibt auch eine effizientere Version:

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

Hier kann ich keine Mapping-Funktion mit dem Ende verketten, ohne den Mapper zu duplizieren oder eine andere lokale Variable zu deklarieren. All dies macht den Code komplizierter als das tatsächlich zu lösende Problem.

Viel lieber würde ich das schreiben:

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

Optional :: existiert natürlich nicht. Was jetzt?

31

Versuche dies:

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

Die Kartenmethode gibt Ihnen einen Optional<Optional<Foo>>. Dann reduziert die orElseGet-Methode dies zurück zu einem Optional<Foo>. Die secondChoice-Methode wird nur ausgewertet, wenn firstChoice() das leere optional zurückgibt.

38
marstran

Sie können das einfach durch ersetzen,

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

Der obige Code kann nur aufgerufen werden, wenn firstChoice.isPresent () false ist.

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

  • Im besten Fall ist die erste Wahl wahr.
  • Der schlimmste Fall ist die erste Wahl, die false zurückgibt, daher eine andere Methode.
5
Suresh Atta

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

@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();
}

Prüfung:

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()));
}

Ausgabe:

foo called
bar called
Optional[bar]
4
Tagir Valeev

Vielleicht so etwas:

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

Von: Verketten von Optionals in Java 8

3
Marthym

Ich war frustriert genug darüber, dass dies in Java 8 nicht unterstützt wurde, und ich auf die Option guava umgestiegen bin, die or :

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

Gibt dieses optional zurück, wenn ein Wert vorhanden ist; secondChoice anders.

1
Erik

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
);
0
Pepe-Soft