java - settitle - ¿Cuál es la forma más elegante de combinar los opcionales?
para que sirve settitle en java (7)
Esto es lo que tengo hasta ahora:
Optional<Foo> firstChoice = firstChoice();
Optional<Foo> secondChoice = secondChoice();
return Optional.ofNullable(firstChoice.orElse(secondChoice.orElse(null)));
Esto me parece a la vez horrible y derrochador. Si firstChoice está presente, estoy calculando innecesariamente secondChoice.
También hay una versión más eficiente:
Optional<Foo> firstChoice = firstChoice();
if(firstChoice.isPresent()) {
return firstChoice;
} else {
return secondChoice();
}
Aquí no puedo encadenar alguna función de mapeo al final sin duplicar el mapeador o declarar otra variable local. Todo esto hace que el código sea más complicado que el problema real que se está resolviendo.
Prefiero estar escribiendo esto:
return firstChoice().alternatively(secondChoice());
Sin embargo Opcional :: por supuesto, obviamente no existe. ¿Ahora que?
Aquí está la generalización de la solución @marstran para cualquier número de opcionales:
@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();
}
Prueba:
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()));
}
Salida:
foo called
bar called
Optional[bar]
Aquí hay una manera que funciona para un número arbitrario de Optional
basados en una API de flujo:
return Arrays.asList(firstChoice, secondChoice).stream()
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst().orElse(null);
No es el más corto. Pero más sencillo y comprensible.
Otra forma es usar firstNonNull()
de Guava de commons-lang si ya está usando una de esas bibliotecas:
firstNonNull(firstChoice.orElse(null), secondChoice.orElse(null));
Cálculos perezosos y número arbitrario de elementos Optional
Stream.<Supplier<Optional<Foo>>>of(
this::firstChoice,
this::secondChoice
).map(
Supplier::get
).filter(
Optional::isPresent
).findFirst(
).orElseGet(
Optional::empty
);
Estaba lo suficientemente frustrado por el hecho de que esto no estaba soportado en Java 8, que volví a los opcionales de guava que tienen or
:
public abstract Optional<T> or(Optional<? extends T> secondChoice)
Devuelve este opcional si tiene un valor presente; segunda elección en caso contrario.
Prueba esto:
firstChoice().map(Optional::of)
.orElseGet(this::secondChoice);
El método de mapa te da un Optional<Optional<Foo>>
. Luego, el método orElseGet
aplana esto de nuevo a un Optional<Foo>
. El método firstChoice()
solo se evaluará si firstChoice()
devuelve el opcional vacío.
Tal vez algo como esto:
Optional<String> finalChoice = Optional.ofNullable(firstChoice()
.orElseGet(() -> secondChoice()
.orElseGet(() -> null)));
Desde: Chaining Optionals en Java 8
Usted simplemente puede reemplazar eso con,
Optional<Foo> firstChoice = firstChoice();
return firstChoice.isPresent()? firstChoice : secondChoice();
El código anterior no llamará a menos que firstChoice.isPresent () sea falso.
Pero tienes que estar preparado para llamar a ambas funciones para obtener la salida deseada. No hay otra manera de escapar de la comprobación.
- El mejor de los casos es la primera opción, volviendo verdad.
- El peor de los casos será la primera opción devolviendo falso, por lo tanto, otro método requiere una segunda opción