usar new icon example codigo borderfactory java java-8 nullable optional

new - ¿Debería Java 8 getters devolver el tipo opcional?



new icon java (5)

Después de investigar un poco por mi cuenta, he encontrado una serie de cosas que podrían sugerir cuándo es apropiado. La más autorizada es la siguiente cita de un artículo de Oracle:

"Es importante tener en cuenta que la intención de la clase Opcional no es reemplazar todas las referencias nulas . En su lugar, su propósito es ayudar a diseñar API más comprensibles para que al leer la firma de un método, pueda determinar si puede esperar un valor opcional. Esto lo obliga a desempaquetar activamente un opcional para tratar con la ausencia de un valor ". - ¿ oracle.com/technetwork/articles/java/… oracle.com/technetwork/articles/java/…

También encontré este extracto de Java 8 Opcional: Cómo usarlo

"Opcional no está destinado a ser utilizado en estos contextos, ya que no nos comprará nada:

  • en la capa de modelo de dominio (no serializable)
  • en DTOs (mismo motivo)
  • en parámetros de entrada de metodos
  • en los parámetros del constructor "

Lo que también parece plantear algunos puntos válidos.

No pude encontrar ninguna connotación negativa o señales de advertencia para sugerir que debería evitarse la Optional . Creo que la idea general es, si es útil o mejora la usabilidad de su API, úsela.

Optional tipo Optional introducido en Java 8 es algo nuevo para muchos desarrolladores.

¿Un método getter que devuelve el tipo Optional<Foo> en lugar del clásico Foo una buena práctica? Supongamos que el valor puede ser null .


Diría que, en general, es una buena idea usar el tipo opcional para valores de retorno que pueden ser anulables. Sin embargo, supongo que reemplazar los getters clásicos por tipos opcionales causará muchos problemas al trabajar con frameworks (por ejemplo, Hibernate) que se basan en las convenciones de codificación para los getters y setters.


La razón por la que se agregó Optional a Java es porque esto:

return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods()) .stream() .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName()) .filter(m -> Arrays.equals(m.getParameterTypes(), parameterClasses)) .filter(m -> Objects.equals(m.getReturnType(), returnType)) .findFirst() .getOrThrow(() -> new InternalError(...));

es más limpio que esto:

Method matching = Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods()) .stream() .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName()) .filter(m -> Arrays.equals(m.getParameterTypes(), parameterClasses)) .filter(m -> Objects.equals(m.getReturnType(), returnType)) .getFirst(); if (matching == null) throw new InternalError("Enclosing method not found"); return matching;

Mi punto es que Opcional se escribió para admitir la programación funcional , que se agregó a Java al mismo tiempo. (El ejemplo es cortesía de un blog de Brian Goetz . Un ejemplo mejor podría usar el método orElse() , ya que este código arrojará una excepción de todos modos, pero obtendrá la imagen).

Pero ahora, la gente está usando Opcional por una razón muy diferente. Lo están utilizando para solucionar una falla en el diseño del lenguaje. La falla es esta: no hay manera de especificar cuáles de los parámetros y valores de retorno de una API pueden ser nulos. Puede ser mencionado en los javadocs, pero la mayoría de los desarrolladores ni siquiera escriben javadocs para su código, y no muchos revisarán los javadocs mientras escriben. Por lo tanto, esto conduce a una gran cantidad de código que siempre verifica los valores nulos antes de usarlos, aunque a menudo no pueden ser nulos porque ya fueron validados repetidamente nueve o diez veces en la pila de llamadas.

Creo que había una verdadera sed de resolver esta falla, porque muchas personas que vieron la nueva clase opcional asumieron que su propósito era agregar claridad a las API. ¿Cuál es la razón por la que la gente hace preguntas como "¿deberían los contratistas devolver opciones? No, es probable que no deban hacerlo, a menos que espere que se utilice el captador en la programación funcional, lo cual es muy improbable. De hecho, si observa dónde se utiliza Opcional en la API de Java, es principalmente en las clases de Stream, que son el núcleo de la programación funcional. (No lo he comprobado a fondo, pero las clases de Stream pueden ser el único lugar donde se usan).

Si planea usar un getter en un poco de código funcional, podría ser una buena idea tener un getter estándar y un segundo que devuelva Opcional.

Ah, y si necesita que su clase sea serializable, no debe usar Opcional.

Las opciones son una solución muy mala para la falla de la API porque a) son muy detalladas, yb) nunca tuvieron la intención de resolver ese problema en primer lugar.

Una solución mucho mejor para la falla de la API es el Comprobador de Nulidad . Este es un procesador de anotaciones que le permite especificar qué parámetros y valores de retorno pueden ser nulos al anotarlos con @Nullable. De esta manera, el compilador puede escanear el código y averiguar si un valor que realmente puede ser nulo se pasa a un valor donde no se permite nulo. De forma predeterminada, se supone que nada puede ser nulo a menos que esté anotado. De esta manera, no tiene que preocuparse por los valores nulos. Pasar un valor nulo a un parámetro resultará en un error del compilador. La prueba de un objeto para un valor nulo que no puede ser nulo produce una advertencia del compilador. El efecto de esto es cambiar la excepción NullPointerException de un error de tiempo de ejecución a un error de tiempo de compilación.

Esto lo cambia todo.

En cuanto a sus captadores, no utilice Opcional. E intenta diseñar tus clases para que ninguno de los miembros pueda ser nulo. Y tal vez intente agregar el verificador de nulidad a su proyecto y declarar a sus captadores y parámetros de configurador @Nullable si lo necesitan. Solo he hecho esto con nuevos proyectos. Probablemente produce muchas advertencias en proyectos existentes escritos con muchas pruebas superfluas para nulos, por lo que podría ser difícil de adaptar. Pero también atrapará muchos errores. Me encanta. Mi código es mucho más limpio y más confiable debido a ello.

(También hay un nuevo lenguaje que aborda esto. Kotlin, que se compila al código de byte de Java, le permite especificar si un objeto puede ser nulo cuando lo declara. Es un enfoque más claro).

Anexo a la publicación original (versión 2)

Después de pensarlo mucho, llegué a la conclusión de que es aceptable devolver Opcional con una condición: que el valor recuperado podría ser nulo. He visto un montón de código donde las personas habitualmente devuelven Opcional de parte de los que no pueden devolver el valor nulo. Veo esto como una muy mala práctica de codificación que solo agrega complejidad al código, lo que hace que los errores sean más probables. Pero cuando el valor devuelto puede ser realmente nulo, siga adelante y envuélvalo dentro de un Opcional.

Tenga en cuenta que los métodos que están diseñados para la programación funcional y que requieren una referencia de función se escribirán (y deberían) en dos formas, una de las cuales utiliza Opcional. Por ejemplo, Optional.map() y Optional.flatMap() toman referencias de funciones. La primera toma una referencia a un captador ordinario, y la segunda toma una que devuelve Opcional. Entonces no le está haciendo un favor a nadie devolviendo un Opcional donde el valor no puede ser nulo.

Dicho todo esto, sigo viendo que el enfoque utilizado por el Comprobador de Nulidad es la mejor manera de lidiar con los nulos, ya que convierten las NullPointerExceptions de errores de tiempo de ejecución para compilar errores de tiempo.


Por supuesto, la gente hará lo que quiera. Pero teníamos una intención clara al agregar esta función, y no era un tipo de propósito general. Tal vez a muchas personas les hubiera gustado que lo hiciéramos. Nuestra intención era proporcionar un mecanismo limitado para los tipos de retorno de métodos de biblioteca donde era necesario que hubiera una forma clara de representar "ningún resultado", y el uso de un null para los mismos era muy probable que causara errores.

Por ejemplo, probablemente nunca debería usarlo para algo que devuelva una serie de resultados o una lista de resultados; en cambio, devuelve una matriz o lista vacía. Casi nunca debe usarlo como un campo de algo o un parámetro de método.

Creo que usarlo de forma rutinaria como valor de retorno para los captadores definitivamente sería un uso excesivo.

No hay nada de malo en Opcional que deba evitarse, simplemente no es lo que mucha gente desea, y por lo tanto estábamos bastante preocupados por el riesgo de un uso excesivo.

(Anuncio de servicio público: NUNCA llame a Optional.get menos que pueda probar que nunca será nulo; en su lugar, utilice uno de los métodos seguros como orElse o ifPresent . En retrospectiva, deberíamos haber llamado get algo como getOrElseThrowNoSuchElementException o algo que lo haya dejado muy claro. que este fue un método altamente peligroso que socavó todo el propósito de la Optional en primer lugar. Lección aprendida. (ACTUALIZACIÓN: Java 10 tiene Optional.orElseThrow() , que es semánticamente equivalente a get() , pero cuyo nombre es más apropiado. ))


Si está utilizando serializadores modernos y otros marcos que entienden Optional , he encontrado que estas directrices funcionan bien al escribir beans de Entity y capas de dominio:

  1. Si la capa de serialización (generalmente un DB) permite un valor null para una celda en la columna BAR en la tabla FOO , entonces el captador Foo.getBar() puede devolver Optional indicando al desarrollador que este valor puede esperarse que sea nulo y debe manejar esto. Si el DB garantiza que el valor no será nulo, el getter no debería envolver esto en un Optional .
  2. Foo.bar debe ser private y no Optional . Realmente no hay razón para que sea Optional si es private .
  3. El Foo.setBar(String bar) debe tomar el tipo de bar y no Optional . Si está bien usar un argumento null indíquelo en el comentario de JavaDoc. Si no está bien usar una IllegalArgumentException null o alguna lógica de negocios apropiada es, en mi humilde opinión, más apropiada.
  4. Los constructores no necesitan argumentos Optional (por razones similares al punto 3). Generalmente solo incluyo argumentos en el constructor que deben ser no nulos en la base de datos de serialización.

Para hacer que lo anterior sea más eficiente, es posible que desee editar sus plantillas IDE para generar captadores y las plantillas correspondientes para toString() , equals(Obj o) etc. o usar campos directamente para esos (la mayoría de los generadores IDE ya tienen nulos).