La razón que Optional
se agregó 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 viene por cortesía de un blog de Brian Goetz . Un mejor ejemplo podría usar el orElse()
método, ya que este código arrojará una excepción de todos modos, pero se obtiene la imagen).
Pero ahora, las personas usan Opcional por una razón muy diferente. Lo están utilizando para abordar una falla en el diseño del lenguaje. La falla es esta: no hay forma de especificar cuál de los parámetros de una API y los valores de retorno pueden ser nulos. Puede mencionarse en los javadocs, pero la mayoría de los desarrolladores ni siquiera escriben javadocs para su código, y no muchos verifican 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, a pesar de que a menudo no pueden ser nulos porque ya se validaron repetidamente nueve o diez veces en la pila de llamadas.
Creo que hubo una verdadera sed de resolver este defecto, porque muchas personas que vieron la nueva clase Opcional asumieron que su propósito era agregar claridad a las API. Por eso la gente hace preguntas como "¿deberían los getters devolver los opcionales?" No, probablemente no deberían, a menos que espere que se use el getter en la programación funcional, lo cual es muy poco probable. De hecho, si observa dónde se usa Opcional en la API de Java, es principalmente en las clases Stream, que son el núcleo de la programación funcional. (No lo he revisado a fondo, pero las clases Stream pueden ser el único lugar donde se usan).
Si planea usar un captador en un poco de código funcional, podría ser una buena idea tener un captador estándar y un segundo que devuelva Opcional.
Ah, y si necesita que su clase sea serializable, no debe usar Opcional.
Los opcionales son una solución muy mala para la falla de la API porque a) son muy detallados yb) Nunca tuvieron la intención de resolver ese problema en primer lugar.
Una solución mucho mejor a 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 forma, el compilador puede escanear el código y determinar si un valor que realmente puede ser nulo se está pasando a un valor donde no está permitido nulo. Por defecto, se supone que nada está permitido a ser nulo a menos que esté anotado así. De esta manera, no tiene que preocuparse por los valores nulos. Pasar un valor nulo a un parámetro dará como resultado un error del compilador. Probar un objeto para nulo que no puede ser nulo produce una advertencia del compilador. El efecto de esto es cambiar 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 use Opcional. Y trate de diseñar sus clases para que ninguno de los miembros pueda ser nulo. Y tal vez intente agregar el Comprobador de nulidad a su proyecto y declarar sus captadores y parámetros de establecimiento @Nullable si lo necesitan. Solo he hecho esto con nuevos proyectos. Probablemente produce muchas advertencias en proyectos existentes escritos con muchas pruebas superfluas para nulo, por lo que puede ser difícil adaptarlo. Pero también atrapará muchos errores. Me encanta. Mi código es mucho más limpio y más confiable debido a eso.
(También hay un nuevo lenguaje que aborda esto. Kotlin, que compila el código de bytes de Java, le permite especificar si un objeto puede ser nulo cuando lo declara. Es un enfoque más limpio).
Anexo a la publicación original (versión 2)
Después de pensarlo mucho, de mala gana he llegado a la conclusión de que es aceptable devolver Opcional con una condición: que el valor recuperado en realidad podría ser nulo. He visto un montón de código en el que la gente devuelve rutinariamente Opcional de getters que posiblemente no pueden devolver nulo. Veo esto como una práctica de codificación muy mala que solo agrega complejidad al código, lo que hace que los errores sean más probables. Pero cuando el valor devuelto pueda ser realmente nulo, continúe y envuélvalo dentro de Opcional.
Tenga en cuenta que los métodos 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 usa Opcional. Por ejemplo, Optional.map()
y Optional.flatMap()
ambos toman referencias de funciones. El primero toma una referencia a un getter ordinario, y el segundo toma uno que devuelve Opcional. Entonces, no le está haciendo un favor a nadie al devolver un Opcional donde el valor no puede ser nulo.
Habiendo dicho todo eso, sigo viendo que el enfoque utilizado por Nullness Checker es la mejor manera de tratar con nulos, ya que convierten NullPointerExceptions de errores de tiempo de ejecución para compilar errores de tiempo.