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.
orElseGet()
lo que OP necesita, solo que no genera una buena sintaxis en cascada.Respuestas:
Esto es parte de JDK 9 en forma de
or
, que toma aSupplier<Optional<T>>
. Su ejemplo sería entonces:Para más detalles, consulte el Javadoc o esta publicación que escribí.
fuente
ifPresent
. Pero de todos modos, creo que el nombreifPresent
no es bueno de todos modos. Para todos los otros métodos que no lleva “si no” en el nombre (comomap
,filter
,flatMap
), se da a entender que no hacen nada si no hay valor está presente, así que ¿por quéifPresent
...Optional<T> perform(Consumer<T> c)
método para permitir el encadenamientoperform(x).orElseDo(y)
(orElseDo
como alternativa a su propuestaifEmpty
, para ser coherente en tenerelse
en el nombre de todos los métodos que podrían hacer algo por los valores ausentes). Puede imitar esoperform
en Java 9 a través destream().peek(x).findFirst()
aunque es un abuso de la API y todavía no hay forma de ejecutar unRunnable
sin especificar unConsumer
al mismo tiempo ...El enfoque más limpio de "probar servicios" dada la API actual sería:
El aspecto importante no es la cadena (constante) de operaciones que tiene que escribir una vez, sino cuán fácil 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 devuelve un no vacío
Optional
.fuente
.map(Optional::get)
con.findFirst()
hará que sea más fácil "leer", por ejemplo, ¿.filter(Optional::isPresent).findFirst().map(Optional::get)
se puede "leer" como "encontrar el primer elemento en la secuencia para la cual Opcional :: isPresent es verdadero y luego aplanarlo aplicando Opcional :: obtener"?No es bonito, pero esto funcionará:
.map(func).orElseGet(sup)
es un patrón bastante útil para usar conOptional
. Significa "Si estoOptional
contiene valorv
, damefunc(v)
, de lo contrario damesup.get()
".En este caso, llamamos
serviceA(args)
y recibimos unOptional<Result>
. Si esoOptional
contiene valorv
, queremos obtenerloOptional.of(v)
, pero si está vacío, queremos obtenerloserviceB(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)
fuente
() -> {}
no devuelve unOptional
. ¿Qué estás tratando de lograr?or(Supplier<Optional<T>>)
de Java 9.map()
en un vacíoOptional
producirá un vacíoOptional
.Quizás esto es lo que buscas: obtener valor de uno Opcional u otro
De lo contrario, es posible que desee echar un vistazo
Optional.orElseGet
. Aquí hay un ejemplo de lo que creo que buscas:fuente
ofNullable
es lo mejor que he visto.Asumiendo que todavía estás en JDK8, hay varias opciones.
Opción # 1: crea tu propio método auxiliar
P.ej:
Para que puedas hacer:
Opción # 2: usa una biblioteca
Por ejemplo, Google guava's Opcional admite una
or()
operación adecuada (al igual que JDK9), por ejemplo:(Donde cada uno de los servicios regresa
com.google.common.base.Optional
, en lugar dejava.util.Optional
).fuente
Optional<T>.or(Supplier<Optional<T>>)
en los documentos de guayaba. ¿Tienes un enlace para eso?Esto parece un buen ajuste para la coincidencia de patrones y una interfaz de opción más tradicional con algunas y ninguna implementaciones (como las de Javaslang , FunctionalJava ) o una implementación perezosa Quizás en cyclops-react Soy el autor de esta biblioteca.
Con cyclops-react también puede usar la coincidencia de patrones estructurales en tipos JDK. Para Opcional, puede coincidir en los casos presentes y ausentes a través del patrón de visitante . se vería algo así:
fuente