Recientemente nos hemos mudado a Java 8. Ahora, veo aplicaciones inundadas de Optional
objetos.
Antes de Java 8 (Estilo 1)
Employee employee = employeeServive.getEmployee();
if(employee!=null){
System.out.println(employee.getId());
}
Después de Java 8 (Estilo 2)
Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
Employee employee = employeeOptional.get();
System.out.println(employee.getId());
}
No veo ningún valor agregado de Optional<Employee> employeeOptional = employeeService.getEmployee();
cuando el servicio en sí mismo devuelve opcional:
Viniendo de un fondo de Java 6, veo el Estilo 1 como más claro y con menos líneas de código. ¿Hay alguna ventaja real que me falta aquí?
Consolidado de la comprensión de todas las respuestas y más investigación en el blog
employeeOptional.isPresent()
parece estar perdiendo por completo el punto de los tipos de opciones. Según el comentario de @ MikePartridge, esto no es recomendable ni idiomático. Verificando explícitamente si un tipo de opción es algo o nada es un no-no. Se supone que debes simplementemap
oflatMap
sobre ellos.Optional
de Brian Goetz , el arquitecto del lenguaje Java en Oracle.Respuestas:
Style 2 no va a Java 8 lo suficiente como para ver el beneficio completo. No quieres el
if ... use
en absoluto. Ver los ejemplos de Oracle . Siguiendo sus consejos, obtenemos:Estilo 3
O un fragmento más largo
fuente
void ifPresent(Consumer<T>, Runnable)
isPresent
get
ifPresentOrElse(Consumer<? super T>, Runnable)
.Si está utilizando
Optional
como una capa de "compatibilidad" entre una API anterior que aún puede regresarnull
, puede ser útil crear el Opcional (no vacío) en la última etapa de que está seguro de que tiene algo. Por ejemplo, donde escribiste:Yo optaría por:
El punto es que usted sabe que hay un no nulo empleado del servicio , por lo que puede envolver que hasta en un
Optional
conOptional.of()
. Luego, cuando recurrasgetEmployee()
a eso, puedes o no conseguir un empleado. Ese empleado puede (o posiblemente no tener) una identificación. Luego, si terminó con una identificación, desea imprimirla.No hay necesidad de verificar explícitamente cualquier nulo, presencia, etc., en este código.
fuente
(...).ifPresent(aMethod)
másif((...).isPresent()) { aMethod(...); }
? Parece ser simplemente otra sintaxis para hacer casi la misma operación.Set<Children> children = Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet);
Ahora puedo procesarchildren
de manera uniforme, por ejemplo,children.forEach(relative::sendGift);
. Aquellos incluso podrían combinarse:Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet).forEach(relative::sendGift);
. Toda la repetitiva de la comprobación nula, etc., ...Casi no hay valor agregado al tener un
Optional
valor único. Como has visto, eso simplemente reemplaza un cheque pornull
un cheque de presencia.Hay un gran valor agregado en tener una
Collection
de esas cosas. Todos los métodos de transmisión introducidos para reemplazar los bucles de bajo nivel de estilo antiguo entienden lasOptional
cosas y hacen lo correcto al respecto (no las procesan o finalmente devuelven otro desarmadoOptional
). Si maneja n elementos con más frecuencia que con elementos individuales (y el 99% de todos los programas realistas lo hacen), entonces es una gran mejora tener soporte incorporado para las cosas opcionalmente presentes. En el caso ideal, nunca tiene que verificarnull
ni presencia.fuente
Employee getEmployee()
vsOptional<Employee> getEmployee()
. Aunque técnicamente ambos podrían regresarnull
, uno de ellos es mucho más probable que cause problemas..map()
sin tener que buscar nulos en todo el camino es genial. EjOptional.of(service).map(Service::getEmployee).map(Employee::getId).ifPresent(this::submitIdForReview);
.null
.Siempre y cuando use
Optional
como una API eleganteisNotNull()
, entonces sí, no encontrará ninguna diferencia con solo verificarnull
.Cosas que NO debes usar
Optional
paraUsar
Optional
solo para verificar la presencia de un valor es Bad Code ™:Cosas que deberías usar
Optional
paraEvite que un valor no esté presente, produciendo uno cuando sea necesario cuando utilice métodos API de retorno nulo:
O, si crear un nuevo empleado es demasiado costoso:
Además, hacer que todos sepan que sus métodos podrían no devolver un valor (si, por alguna razón, no puede devolver un valor predeterminado como el anterior):
Y, por último, están todos los métodos de flujo que ya se han explicado en otras respuestas, aunque esos no son realmente los que usted decide usar,
Optional
sino que utiliza losOptional
proporcionados por otra persona.fuente
Optional
como una oportunidad perdida. "Aquí, tengo esta nuevaOptional
cosa que hice para que te veas obligado a verificar valores nulos. Ah, y por cierto ... agregué unget()
método para que no tengas que verificar nada;);)" . (...)Optional
es agradable como un letrero de neón "ESTO PODRÍA SER NULO", pero la simple existencia de suget()
método lo paraliza » . Pero OP estaba pidiendo razones para usarOptional
, no razones en contra o fallas de diseño.Employee employee = Optional.ofNullable(employeeService.getEmployee());
En su tercer fragmento, ¿no debería ser el tipoOptional<Employee>
?get
es si tiene que interactuar con algún código heredado. No puedo pensar en muchas razones válidas en el nuevo código para usarget
. Dicho esto, al interactuar con el código heredado a menudo no hay alternativa, pero aún así tiene un punto que la existencia deget
principiantes hace que el código sea incorrecto.Map
una vez y no estoy al tanto de que alguien haga un cambio tan drástico no compatible con versiones anteriores, así que asegúrese de que pueda diseñar intencionalmente API incorrectasOptional
, no estoy seguro de lo que eso prueba. SinOptional
embargo, tendría sentido para algúntryGet
método.Map
es un ejemplo con el que todos están familiarizados. Por supuesto, no puedenMap.get
devolver un Opcional debido a la compatibilidad con versiones anteriores, pero puede imaginar fácilmente otra clase similar a un Mapa que no tendría tales restricciones de compatibilidad. Decirclass UserRepository {Optional<User> getUserDetails(int userID) {...}}
. Ahora desea obtener los detalles del usuario conectado, y sabe que existen porque lograron iniciar sesión y su sistema nunca elimina usuarios. Entonces lo hacestheUserRepository.getUserDetails(loggedInUserID).get()
.El punto clave para mí al usar Opcional es mantenerse claro cuando el desarrollador necesita verificar si el valor de retorno de la función o no.
fuente
Su principal error es que todavía está pensando en términos más procesales. Esto no pretende ser una crítica de ti como persona, es simplemente una observación. Pensar en términos más funcionales viene con tiempo y práctica, y por lo tanto, los métodos están presentes y se ven como las cosas correctas más obvias para usted. Su error secundario secundario es crear su Opcional dentro de su método. El Opcional está destinado a ayudar a documentar que algo puede o no devolver un valor. Puede que no consigas nada.
Esto te ha llevado a escribir un código perfectamente legible que parece perfectamente razonable, pero te sedujeron las viles tentadoras gemelas que se ponen y están presentes.
Por supuesto, la pregunta se convierte rápidamente en "¿por qué está presente y llega hasta allí?"
Una cosa que mucha gente extraña aquí es que Presente () es que no es algo creado para un nuevo código escrito por personas totalmente conscientes de cuán malditas son las lambdas útiles y a las que les gusta lo funcional.
Sin embargo, nos da algunos (dos) beneficios buenos, geniales y glamorosos (?):
El primero es bastante simple.
Imagine que tiene una API que se ve así:
Y tenía una clase heredada usando esto como tal (complete los espacios en blanco):
Un ejemplo artificial para estar seguro. Pero ten paciencia conmigo aquí.
Java 8 se ha lanzado ahora y estamos luchando por subir a bordo. Entonces, una de las cosas que hacemos es que queremos reemplazar nuestra interfaz anterior con algo que devuelva Opcional. ¿Por qué? Porque como alguien más ya ha mencionado amablemente: esto elimina las conjeturas de si algo puede ser nulo o no. Esto ya ha sido señalado por otros. Pero ahora tenemos un problema. Imagine que tenemos (disculpe mientras presiono alt + F7 en un método inocente), 46 lugares donde se llama a este método en código heredado bien probado que de lo contrario hace un excelente trabajo. Ahora tienes que actualizar todo esto.
ESTO es donde brilla IsPresent.
Porque ahora: Snickers lastSnickers = snickersCounter.lastConsumedSnickers (); if (null == lastSnickers) {lanzar una nueva NoSuchSnickersException (); } else {consumer.giveDiabetes (lastSnickers); }
se convierte en:
Y este es un cambio simple que puede darle al nuevo junior: puede hacer algo útil y podrá explorar la base de código al mismo tiempo. ganar-ganar Después de todo, algo similar a este patrón está bastante extendido. Y ahora no tiene que volver a escribir el código para usar lambdas ni nada. (En este caso particular, sería trivial, pero dejo pensar ejemplos en los que sería difícil como ejercicio para el lector).
Tenga en cuenta que esto significa que la forma en que lo hizo es esencialmente una forma de lidiar con el código heredado sin hacer reescrituras costosas. Entonces, ¿qué pasa con el nuevo código?
Bueno, en su caso, donde solo desea imprimir algo, simplemente haría:
snickersCounter.lastConsumedSnickers (). ifPresent (System.out :: println);
Lo cual es bastante simple y perfectamente claro. El punto que burbujea lentamente hacia la superficie es que existen casos de uso para get () y isPresent (). Están ahí para permitirle modificar mecánicamente el código existente para usar los tipos más nuevos sin pensar demasiado en ello. Por lo tanto, lo que está haciendo está mal orientado de las siguientes maneras:
Si desea utilizar Opcional como una simple verificación de seguridad nula, lo que debería haber hecho es simplemente esto:
Por supuesto, la buena versión de esto se ve así:
Por cierto, aunque de ninguna manera es obligatorio, recomiendo usar una nueva línea por operación, para que sea más fácil de leer. Fácil de leer y comprender mejor que la concisión cualquier día de la semana.
Por supuesto, este es un ejemplo muy simple en el que es fácil entender todo lo que estamos tratando de hacer. No siempre es así de simple en la vida real. Pero observe cómo en este ejemplo, lo que estamos expresando son nuestras intenciones. Queremos OBTENER el empleado, OBTENER su identificación y, si es posible, imprimirlo. Esta es la segunda gran victoria con Opcional. Nos permite crear un código más claro. También creo que hacer cosas como hacer un método que haga un montón de cosas para que puedas alimentarlo a un mapa es, en general, una buena idea.
fuente
map(x).ifPresent(f)
simplemente no tiene el mismo tipo de sentido intuitivo queif(o != null) f(x)
tiene. Laif
versión es perfectamente clara. Este es otro caso de personas saltando en un carro de banda popular, * no * un caso de progreso real.Optional
. Me refería solo al uso de opcional en este caso específico, y más aún a la legibilidad, ya que varias personas actúan como si este formato fuera igual o más legible queif
.isPresent
yget
sea lo más realista posible) mejorar la seguridad . Es más difícil escribir código incorrecto que no puede verificar la ausencia de un valor si el tipo esOptional<Whatever>
más que usar un nuloWhatever
.get
, se verá obligado a elegir qué hacer cuando ocurra un valor faltante: usaríaorElse()
(oorElseGet()
) para proporcionar un valor predeterminado oorElseThrow()
arrojar un valor elegido. excepción que documenta la naturaleza del problema , que es mejor que un NPE genérico que no tiene sentido hasta que se profundiza en la traza de la pila.No veo beneficio en el Estilo 2. Todavía debe darse cuenta de que necesita la verificación nula y ahora es aún más grande, por lo tanto, menos legible.
Sería un mejor estilo, si employeeServive.getEmployee () devolvería Opcional y luego el código sería:
De esta manera no puede llamar empleeeServive.getEmployee (). GetId () y observa que debe manejar el valor opcional de alguna manera. Y si en todos sus métodos de base de código nunca devuelve nulo (o casi nunca con excepciones bien conocidas por su equipo a esta regla), aumentará la seguridad contra NPE.
fuente
isPresent()
. Usamos opcional para no tener que probar.isPresent()
. Este es el enfoque incorrecto de los opcionales. Usomap
o en suflatMap
lugar.ifPresent
) es solo otra forma de probar.ifPresent(x)
es tanto una prueba comoif(present) {x}
. El valor de retorno es irrelevante.Creo que el mayor beneficio es que si su firma del método devuelve una
Employee
que no comprobar NULL. Usted sabe con esa firma que se garantiza que devolverá a un empleado. No necesita manejar un caso de falla. Con un opcional sabes que lo haces.En el código que he visto, hay cheques nulos que nunca fallarán, ya que las personas no quieren rastrear el código para determinar si es posible un nulo, por lo que arrojan cheques nulos en todas partes a la defensiva. Esto hace que el código sea un poco más lento, pero lo más importante es que hace que el código sea mucho más ruidoso.
Sin embargo, para que esto funcione, debe aplicar este patrón de manera consistente.
fuente
@Nullable
y@ReturnValuesAreNonnullByDefault
junto con el análisis estático.package-info.java
con@ReturnValuesAreNonnullByDefault
todos mis paquetes, por lo que todo lo que tengo que hacer es añadir@Nullable
en el que tiene que cambiar aOptional
. Esto significa menos caracteres, sin basura adicional y sin sobrecarga de tiempo de ejecución. No tengo que buscar la documentación como se muestra al pasar el mouse sobre el método. La herramienta de análisis estático detecta errores y posteriores cambios de nulabilidad.Optional
es que agrega una forma alternativa de lidiar con la nulabilidad. Si estuvo en Java desde el principio, puede estar bien, pero ahora es solo un dolor. Seguir el camino de Kotlin sería en mi humilde opinión mucho mejor. +++ Tenga en cuenta queList<@Nullable String>
todavía es una lista de cadenas, mientrasList<Optional<String>>
que no lo es.Mi respuesta es: simplemente no. Al menos piénsalo dos veces si es realmente una mejora
Una expresión (tomada de otra respuesta) como
parece ser la forma funcional correcta de tratar con los métodos de retorno originalmente anulables. Se ve bien, nunca arroja y nunca te hace pensar en manejar el caso "ausente".
Se ve mejor que la forma antigua
lo que hace obvio que puede haber
else
cláusulas.El estilo funcional
orElse
no siempre es suficiente)En ningún caso debe usarse como panacea. Si fuera universalmente aplicable, entonces la semántica del
.
operador debería ser reemplazada por la semántica del?.
operador , el NPE olvidado y todos los problemas ignorados.Si bien soy un gran fanático de Java, debo decir que el estilo funcional es solo el sustituto de la declaración de un pobre hombre de Java
bien conocido de otros idiomas. ¿Estamos de acuerdo en que esta declaración
fuente
employeeService.getEmployee().getId().apply(id => System.out.println(id));
y haga que sea nulo seguro una vez que use el operador de navegación segura y una vez que use elOptional
. Claro, podríamos llamarlo un "detalle de implementación", pero la implementación es importante. Hay casos en que los lamdas hacen que el código sea más simple y agradable, pero aquí es solo un feo abuso.Optional.of(employeeService).map(EmployeeService::getEmployee).map(Employee::getId).ifPresent(System.out::println);
característica del lenguaje:employeeService.getEmployee()?.getId()?.apply(id => System.out.println(id));
. Dos sintaxis, pero terminan pareciéndose mucho a mí. Y el "concepto" esOptional
tambiénNullable
conocido como akaMaybe
+++
Lo que se abusa son lamdas para manejar la nulabilidad. Los lamdas están bien, peroOptional
no lo están. La anulabilidad simplemente necesita un soporte de idioma adecuado como en Kotlin. Otro problema:List<Optional<String>>
no está relacionado conList<String>
dónde necesitaríamos que estén relacionados.Opcional es un concepto (un tipo de orden superior) que proviene de la programación funcional. Usarlo elimina la comprobación nula anidada y te salva de la pirámide de la fatalidad.
fuente