que - optional type in java 8
Opcional o Else Opcional en Java (6)
Asumiendo que todavía estás en JDK8, hay varias opciones.
Opción # 1: crea tu propio método auxiliar
P.ej:
public class Optionals {
static <T> Optional<T> or(Supplier<Optional<T>>... optionals) {
return Arrays.stream(optionals)
.map(Supplier::get)
.filter(Optional::isPresent)
.findFirst()
.orElseGet(Optional::empty);
}
}
Para que puedas hacer:
return Optionals.or(
()-> serviceA(args),
()-> serviceB(args),
()-> serviceC(args),
()-> serviceD(args)
);
Opción # 2: usa una biblioteca
Por ejemplo, Google guava''s Opcional admite una operación adecuada
or()
(como JDK9), por ejemplo:
return serviceA(args)
.or(() -> serviceB(args))
.or(() -> serviceC(args))
.or(() -> serviceD(args));
(Donde cada uno de los servicios devuelve
com.google.common.base.Optional
, en lugar de
java.util.Optional
).
He estado trabajando con el nuevo tipo Opcional en Java 8 , y me he encontrado con lo que parece una operación común que no es compatible funcionalmente: un "orElseOptional"
Considere el siguiente patrón:
Optional<Result> resultFromServiceA = serviceA(args);
if (resultFromServiceA.isPresent) return result;
else {
Optional<Result> resultFromServiceB = serviceB(args);
if (resultFromServiceB.isPresent) return resultFromServiceB;
else return serviceC(args);
}
Hay muchas formas de este patrón, pero se reduce a querer un "o Else" en un opcional que toma una función que produce un nuevo opcional, llamado solo si el actual no existe.
Su implementación se vería así:
public Optional<T> orElse(Supplier<Optional<? extends T>> otherSupplier) {
return value != null ? this : other.get();
}
Tengo curiosidad por saber si hay una razón por la que dicho método no existe, si solo estoy usando Opcional de forma no intencionada, y qué otras formas han encontrado las personas para tratar este caso.
Debo decir que creo que las soluciones que involucran clases / métodos de utilidad personalizados no son elegantes porque las personas que trabajan con mi código no necesariamente sabrán que existen.
Además, si alguien sabe, ¿se incluirá dicho método en JDK 9, y dónde podría proponerlo? Esto me parece una omisión bastante evidente de la API.
El enfoque más limpio de "probar servicios" dada la API actual sería:
Optional<Result> o = Stream.<Supplier<Optional<Result>>>of(
()->serviceA(args),
()->serviceB(args),
()->serviceC(args),
()->serviceD(args))
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
El aspecto importante no es la cadena (constante) de operaciones que tiene que escribir una vez, sino lo fácil que es agregar otro servicio (o modificar la lista de servicios es general).
Aquí, agregar o eliminar un solo
()->serviceX(args)
es suficiente.
Debido a la evaluación diferida de las secuencias, no se invocará ningún servicio si un servicio anterior devolvió un
Optional
no vacío.
Esto es parte de JDK 9 en forma de
or
, que toma un
Supplier<Optional<T>>
.
Su ejemplo sería entonces:
return serviceA(args)
.or(() -> serviceB(args))
.or(() -> serviceC(args));
Para más detalles, consulte el Javadoc o esta publicación que escribí.
Esto parece un buen ajuste para la coincidencia de patrones y una interfaz de opción más tradicional con implementaciones de Some and None (como las de Javaslang , FunctionalJava ) o una implementación de Maybe flojo en cyclops-react Soy el autor de esta biblioteca.
Con cyclops-react también puede usar la coincidencia de patrones estructurales en los tipos JDK. Para Opcional, puede coincidir en los casos presentes y ausentes a través del patrón de visitante . se vería así:
import static com.aol.cyclops.Matchables.optional;
optional(serviceA(args)).visit(some -> some ,
() -> optional(serviceB(args)).visit(some -> some,
() -> serviceC(args)));
No es bonito, pero esto funcionará:
return serviceA(args)
.map(Optional::of).orElseGet(() -> serviceB(args))
.map(Optional::of).orElseGet(() -> serviceC(args))
.map(Optional::of).orElseGet(() -> serviceD(args));
.map(func).orElseGet(sup)
es un patrón bastante útil para usar con
Optional
.
Significa "Si este
Optional
contiene el valor
v
, dame
func(v)
, de lo contrario dame
sup.get()
".
En este caso, llamamos al
serviceA(args)
y obtenemos un
Optional<Result>
.
Si ese
Optional
contiene el valor
v
, queremos obtener
Optional.of(v)
, pero si está vacío, queremos obtener el servicio
serviceB(args)
.
Enjuague y repita con más alternativas.
Otros usos de este patrón son
-
.map(Stream::of).orElseGet(Stream::empty)
-
.map(Collections::singleton).orElseGet(Collections::emptySet)
Quizás esto es lo que buscas: obtener valor de uno opcional u otro
De lo contrario, es posible que desee echar un vistazo a
Optional.orElseGet
.
Aquí hay un ejemplo de lo que
creo
que buscas:
result = Optional.ofNullable(serviceA().orElseGet(
() -> serviceB().orElseGet(
() -> serviceC().orElse(null))));