Después de haber estado utilizando Java 8 ahora durante más de 6 meses, estoy bastante contento con los nuevos cambios de API. Un área en la que aún no confío es cuándo usarla Optional
. Parece que me pongo entre querer usarlo en todas partes donde algo pueda estar null
, y en ninguna parte.
Parece que hay muchas situaciones en las que podría usarlo, y nunca estoy seguro de si agrega beneficios (legibilidad / seguridad nula) o solo causa una sobrecarga adicional.
Entonces, tengo algunos ejemplos, y estaría interesado en los pensamientos de la comunidad sobre si Optional
es beneficioso.
1 - Como un método de devolución de método público cuando el método podría devolver null
:
public Optional<Foo> findFoo(String id);
2 - Como parámetro de método cuando el parámetro puede ser null
:
public Foo doSomething(String id, Optional<Bar> barOptional);
3 - Como miembro opcional de un bean:
public class Book {
private List<Pages> pages;
private Optional<Index> index;
}
4 - En Collections
:
En general no pienso:
List<Optional<Foo>>
agrega cualquier cosa, especialmente porque se puede usar filter()
para eliminar null
valores, etc., pero ¿hay algún buen uso Optional
en las colecciones?
¿Algún caso que me haya perdido?
Map<Character, String>
. Si no hay una sustitución puedo usar esto:Optional.ofNullable(map.get(c)).orElse(String.valueOf(c))
. También tenga en cuenta que Opcional fue robado de Guava y tiene una sintaxis mucho mejor:Optional.fromNullable(map.get(c)).or(String.valueOf(c));
.filter(Optional::absent)
eliminar los "valores nulos"getOrDefault()
?Respuestas:
El punto principal de
Optional
es proporcionar un medio para que una función devuelva un valor para indicar la ausencia de un valor de retorno. Ver esta discusión . Esto permite que la persona que llama continúe una cadena de llamadas de métodos fluidas.Esto coincide más estrechamente con el caso de uso # 1 en la pregunta del OP. Sin embargo , la ausencia de un valor es una formulación más precisa que nula ya que algo así
IntStream.findFirst
nunca podría volver nula.Para el caso de uso # 2 , pasar un argumento opcional a un método, esto podría funcionar, pero es bastante torpe. Supongamos que tiene un método que toma una cadena seguida de una segunda cadena opcional. Aceptar un
Optional
como el segundo argumento daría como resultado un código como este:Incluso aceptar nulo es mejor:
Probablemente lo mejor es tener un método sobrecargado que acepte un único argumento de cadena y proporcione un valor predeterminado para el segundo:
Esto tiene limitaciones, pero es mucho mejor que cualquiera de los anteriores.
Los casos de uso n.º 3 y n.º 4 , que tienen un
Optional
campo de clase o una estructura de datos, se consideran un mal uso de la API. Primero, va en contra del objetivo principal de diseñoOptional
como se indica en la parte superior. En segundo lugar, no agrega ningún valor.Hay tres formas de lidiar con la ausencia de un valor en un
Optional
: para proporcionar un valor sustituto, para llamar a una función para proporcionar un valor sustituto o para lanzar una excepción. Si está almacenando en un campo, lo haría en el momento de la inicialización o la asignación. Si está agregando valores a una lista, como lo mencionó el OP, tiene la opción adicional de simplemente no agregar el valor, "aplanando" los valores ausentes.Estoy seguro de que alguien podría idear algunos casos artificiales en los que realmente quieran almacenar una
Optional
en un campo o una colección, pero en general, es mejor evitar hacerlo.fuente
Optional
campo como cuando las cosas se deben hacer o no con la presencia o ausencia de un valor.if(foo == null)
internamente sería mejor que simplemente almacenar el campo comooptional
. Y no veo por qué llamar explícitamentegetOptionalFoo()
internamente es mejor que simplemente almacenar el campo como unOptional
. Además, si un campo puede sernull
y un campo no puede sernull
, ¿por qué no comunicarlo al compilador haciéndolosOptional
o noOptional
?Optional<>
expertos: "no almacenarOptional<>
en un campo". Realmente estoy tratando de entender el punto de esta recomendación. Considere un árbol DOM donde un nodo podría tener un padre o no. Parece dentro del caso de uso queNode.getParent()
volveríaOptional<Node>
. ¿Realmente se espera que almacene unnull
y ajuste el resultado cada vez? ¿Por qué? ¿Qué me compra esta ineficiencia adicional, aparte de hacer que mi código se vea feo?Optional<Node> getParent() { return Optional.ofNullable(parent); }
. ¡Esto parece caro porque asigna unaOptional
cada vez! Pero si la persona que llama lo desempaqueta inmediatamente, el objeto tiene una vida extremadamente corta y nunca sale del espacio edén. Posiblemente incluso podría ser eliminado por el análisis de escape del JIT. La respuesta final es "depende" como siempre, pero el usoOptional
en campos potencialmente aumenta el uso de memoria y ralentiza el recorrido de la estructura de datos. Y finalmente creo que desordenan el código, pero esto es cuestión de gustos.Llego tarde al juego, pero por lo que vale, quiero agregar mis 2 centavos. Van en contra del objetivo de diseño de
Optional
, que está bien resumido por la respuesta de Stuart Marks , pero todavía estoy convencido de su validez (obviamente).Usar opcional en todas partes
En general
Escribí una publicación de blog
Optional
completa sobre el uso, pero básicamente se reduce a esto:Optional
lugar denull
Las dos primeras excepciones pueden reducir la sobrecarga percibida de envolver y desenvolver referencias en
Optional
. Se eligen de manera que un valor nulo nunca pueda pasar legalmente un límite de una instancia a otra.Tenga en cuenta que esto casi nunca permitirá
Optional
s en colecciones que es casi tan malo comonull
s. Solo no lo hagas. ;)Con respecto a sus preguntas
Ventajas
Hacer esto reduce la presencia de
null
s en su base de código, aunque no los erradica. Pero ese ni siquiera es el punto principal. Hay otras ventajas importantes:Aclara la intención
El uso
Optional
expresa claramente que la variable es, bueno, opcional. Cualquier lector de su código o consumidor de su API será derrotado con el hecho de que podría no haber nada allí y que es necesario realizar una verificación antes de acceder al valor.Elimina la incertidumbre
Sin
Optional
el significado de unnull
hecho no está claro. Podría ser una representación legal de un estado (verMap.get
) o un error de implementación como una inicialización faltante o fallida.Esto cambia dramáticamente con el uso persistente de
Optional
. Aquí, ya la aparición denull
significa la presencia de un error. (Porque si se permitiera que faltara el valor,Optional
se habría utilizado un.) Esto hace que la depuración de una excepción de puntero nulo sea mucho más fácil ya que la pregunta sobre el significado de estonull
ya está respondida.Más cheques nulos
Ahora que nada puede ser
null
más, esto puede hacerse cumplir en todas partes. Ya sea con anotaciones, aserciones o comprobaciones simples, nunca tendrá que pensar si este argumento o ese tipo de retorno pueden ser nulos. No puede!Desventajas
Por supuesto, no hay bala de plata ...
Actuación
Ajustar los valores (especialmente los primitivos) a una instancia adicional puede degradar el rendimiento. En lazos cerrados, esto podría volverse notable o incluso peor.
Tenga en cuenta que el compilador podría evitar la referencia adicional para vidas cortas de
Optional
s. En Java 10 , los tipos de valor pueden reducir o eliminar aún más la penalización.Publicación por entregas
Optional
no es serializable, pero una solución alternativa no es demasiado complicada.Invariancia
Debido a la invariabilidad de los tipos genéricos en Java, ciertas operaciones se vuelven engorrosas cuando el tipo de valor real se inserta en un argumento de tipo genérico. Aquí se da un ejemplo (ver "Polimorfismo paramétrico") .
fuente
List
s. Este es el caso cuando es importante que cierto elemento esté ubicado en un cierto índice, pero el elemento puede estar presente o no. Eso se comunica claramente por unList<Optional<T>>
tipo. Si, por otro lado, solo es importante qué objetos que son miembros alguna colección, entonces no tiene sentido.Optional
ser pasado a un método puede sernull
. Entonces, ¿qué has ganado? Ahora tienes que hacer dos cheques. Ver stackoverflow.com/a/31923042/650176Optional
(lo que debería ser el caso si estás dispuesto a pasarlo como argumento)null
nunca es un valor legal. Entonces, llamar a un método con un parámetro explícitonull
es obviamente un error.Personalmente, prefiero usar la herramienta de inspección de código de IntelliJ para usar
@NotNull
y@Nullable
verificar, ya que estos son en gran parte tiempo de compilación (puede tener algunas verificaciones de tiempo de ejecución) Esto tiene una sobrecarga menor en términos de legibilidad de código y rendimiento de tiempo de ejecución. No es tan riguroso como usar Opcional, sin embargo, esta falta de rigor debería estar respaldada por pruebas unitarias decentes.Esto funciona con Java 5 y no es necesario ajustar y desenvolver valores. (o crear objetos de envoltura)
fuente
@Nonnull
personalmente - trabaja con FindBugs;)@Nonnull @NotNull etc
Creo que Guava Opcional y su página wiki lo explican bastante bien:
Optional
agrega algo de sobrecarga, pero creo que su clara ventaja es hacer explícito que un objeto podría estar ausente y exige que los programadores manejen la situación. Impide que alguien olvide el querido!= null
cheque.Tomando el ejemplo de 2 , creo que es un código mucho más explícito para escribir:
que
Para mí,
Optional
mejor captura el hecho de que no hay una tarjeta de sonido presente.Mis 2 ¢ sobre tus puntos:
public Optional<Foo> findFoo(String id);
- No estoy seguro de esto. Tal vez devolvería unResult<Foo>
que podría estar vacío o contener unFoo
. Es un concepto similar, pero en realidad noOptional
.public Foo doSomething(String id, Optional<Bar> barOptional);
- Preferiría @Nullable y una comprobación de findbugs, como en la respuesta de Peter Lawrey - vea también esta discusión .Optional<Index> getIndex()
para indicar explícitamente que el libro podría no tener un índice.En general, trataría de minimizar el paso
null
s. (Una vez quemada ...) Creo que vale la pena encontrar las abstracciones apropiadas e indicar a los demás programadores qué representa un cierto valor de retorno.fuente
soundcard.ifPresent(System.out::println)
. LlamarisPresent
es semánticamente lo mismo que verificarnull
.if(soundcard != null && soundcard.isPresent())
. Aunque hacer una API que devuelva un Opcional y también podría ser nulo es algo que espero que nadie haga.Del tutorial de Oracle :
fuente
Aquí hay un buen artículo que muestra la utilidad del caso de uso # 1. Hay este código
se transforma a esto
utilizando Opcional como valor de retorno de los métodos getter respectivos .
fuente
Aquí hay un uso interesante (creo) para ... Pruebas.
Tengo la intención de probar uno de mis proyectos y, por lo tanto, construir afirmaciones; solo hay cosas que tengo que verificar y otras que no.
Por lo tanto, construyo cosas para afirmar y uso una afirmación para verificarlas, así:
En la clase NodeAssert, hago esto:
¡Lo que significa que la afirmación realmente solo se activa si quiero verificar la etiqueta!
fuente
Optional
clase le permite evitar el usonull
y proporcionar una mejor alternativa:Esto alienta al desarrollador a hacer comprobaciones de presencia para evitar no ser capturado
NullPointerException
.La API se documenta mejor porque es posible ver dónde esperar los valores que pueden estar ausentes.
Optional
proporciona API conveniente para trabajar más con el objetoisPresent()
:;get()
;orElse()
;orElseGet()
;orElseThrow()
;map()
;filter()
;flatmap()
.Además, muchos marcos utilizan activamente este tipo de datos y lo devuelven desde su API.
fuente
En Java, simplemente no los use a menos que sea adicto a la programación funcional.
No tienen lugar como argumentos de método (prometo que alguien algún día le pasará una opción nula, no solo una opción que esté vacía).
Tienen sentido para los valores de retorno, pero invitan a la clase del cliente a seguir estirando la cadena de desarrollo del comportamiento.
FP y las cadenas tienen poco lugar en un lenguaje imperativo como Java porque hace que sea muy difícil depurar, no solo leer. Cuando te acercas a la línea, no puedes conocer el estado ni la intención del programa; tiene que intervenir para resolverlo (en un código que a menudo no es suyo y muchos marcos de pila profundos a pesar de los filtros de pasos) y debe agregar muchos puntos de interrupción para asegurarse de que pueda detenerse en el código / lambda que agregó, en lugar de simplemente caminar por las líneas triviales if / else / call.
Si desea una programación funcional, elija algo más que Java y espere que tenga las herramientas para depurar eso.
fuente
No creo que Opcional sea un sustituto general de los métodos que potencialmente devuelven valores nulos.
La idea básica es: la ausencia de un valor no significa que esté potencialmente disponible en el futuro. Es una diferencia entre findById (-1) y findById (67).
La información principal de los opcionales para la persona que llama es que no puede contar con el valor dado, pero puede estar disponible en algún momento. Tal vez desaparecerá nuevamente y volverá más tarde una vez más. Es como un interruptor de encendido / apagado. Tiene la "opción" para encender o apagar la luz. Pero no tiene opción si no tiene una luz para encender.
Por lo tanto, me resulta demasiado desordenado introducir los opcionales en todas partes donde anteriormente se devolvió nulo. Todavía usaré nulo, pero solo en áreas restringidas como la raíz de un árbol, inicialización perezosa y métodos de búsqueda explícitos.
fuente
Parece
Optional
sólo es útil si el tipo T en opcional es un tipo simple comoint
,long
,char
, etc Para las clases "reales", que no tiene sentido para mí, como se puede utilizar unnull
valor de todos modos.Creo que fue tomado de aquí (o de otro concepto de lenguaje similar).
Nullable<T>
En C #, esto
Nullable<T>
se introdujo hace mucho tiempo para ajustar los tipos de valor.fuente
Optional
de hechojava.util.Optional
.An tiene una semántica similar a una instancia no modificable del patrón de diseño Iterator :
Optional
isPresent()
)get()
) si se refiere a un objetonext()
método).Por lo tanto, considere devolver o pasar un
Optional
contexto en el que anteriormente podría haber considerado utilizar un JavaIterator
.fuente
Java SE 8 presenta una nueva clase llamada java.util.Optional,
Puede crear un Opcional u Opcional vacío con valor nulo.
Y aquí hay un Opcional con un valor no nulo:
Hacer algo si hay un valor presente
Ahora que tiene un objeto Opcional, puede acceder a los métodos disponibles para tratar explícitamente la presencia o ausencia de valores. En lugar de tener que recordar hacer una verificación nula, de la siguiente manera:
Puede usar el método ifPresent () de la siguiente manera:
fuente