Tengo un código que falla debido a NullPointerException. Se llama a un método en el objeto donde el objeto no existe.
Sin embargo, esto me llevó a pensar en la mejor manera de solucionar esto. ¿Siempre codifico a la defensiva los nulos para que pueda probar el código futuro para las excepciones de puntero nulo, o debo corregir la causa del nulo para que no ocurra en sentido descendente?
¿Cuáles son tus pensamientos?
Respuestas:
Si nulo es un parámetro de entrada razonable para su método, corríjalo. Si no, arregle la llamada. "Razonable" es un término flexible, por lo que propongo la siguiente prueba: ¿Cómo debe el método entregar una entrada nula? Si encuentra más de una respuesta posible, entonces nulo no es una entrada razonable.
fuente
Precondition.checkNotNull(...)
. Ver stackoverflow.com/questions/3022319/…IllegalArgumentException
cuando sea nulo. Esto indica a las personas que llaman sobre el método que el error está en su código (en lugar de en el método en sí).No use nulo, use Opcional
Como ha señalado, uno de los mayores problemas con
null
Java es que puede usarse todas partes , o al menos para todos los tipos de referencia.Es imposible decir que podría ser
null
y qué no podría ser.Java 8 presenta un patrón mucho mejor:
Optional
.Y ejemplo de Oracle:
Si cada uno de estos puede o no devolver un valor exitoso, puede cambiar las API a
Optional
s:Al codificar explícitamente la opcionalidad en el tipo, sus interfaces serán mucho mejores y su código será más limpio.
Si no está utilizando Java 8, puede mirar
com.google.common.base.Optional
en Google Guava.Una buena explicación del equipo de Guava: https://github.com/google/guava/wiki/UsingAndAvoidingNullExplained
Una explicación más general de las desventajas de nulo, con ejemplos de varios idiomas: https://www.lucidchart.com/techblog/2015/08/31/the-worst-mistake-of-computer-science/
@Nonnull, @Nullable
Java 8 agrega estas anotaciones para ayudar a las herramientas de verificación de código como IDE a detectar problemas. Son bastante limitados en su efectividad.
Comprueba cuándo tiene sentido
No escriba el 50% de su código comprobando nulo, especialmente si no hay nada sensato que su código pueda hacer con un
null
valor.Por otro lado, si
null
pudiera usarse y significar algo, asegúrese de usarlo.En última instancia, obviamente no puede eliminar
null
de Java. Recomiendo sustituir laOptional
abstracción siempre que sea posible, y comprobarnull
esas otras veces que puede hacer algo razonable al respecto.fuente
NullPointerException
? ANullPointerException
puede suceder literalmente cada vez que llama a un método de instancia en Java. Lo tendríasthrows NullPointerException
en casi todos los métodos.Hay varias maneras de manejar esto, agregarle código a su código
if (obj != null) {}
no es ideal, es desordenado, agrega ruido al leer el código más adelante durante el ciclo de mantenimiento y es propenso a errores, ya que es fácil olvidarse de hacer esta envoltura de la placa de la caldera.Depende de si desea que el código continúe ejecutándose silenciosamente o falle. Es el
null
error o una condición esperada.Lo que es nulo
En cada definición y caso, Null representa la falta absoluta de datos. Los valores nulos en las bases de datos representan la falta de un valor para esa columna. un nulo
String
no es lo mismo que un vacíoString
, un nuloint
no es lo mismo que CERO, en teoría. En la práctica "depende". VacíoString
puede ser una buenaNull Object
implementación para la clase String, yaInteger
que depende de la lógica empresarial.Las alternativas son:
El
Null Object
patrón Cree una instancia de su objeto que represente elnull
estado e inicialice todas las referencias a ese tipo con una referencia a laNull
implementación. Esto es útil para objetos de tipo de valor simple que no tienen muchas referencias a otros objetos que también podrían sernull
y se espera que seanull
un estado válido.Utilice herramientas orientadas a
Null Checker
aspectos para tejer métodos con un aspecto que evite que los parámetros sean nulos. Esto es para casos dondenull
hay un error.Uso
assert()
no mucho mejor que elif (obj != null){}
pero menos ruido.Utilice una herramienta de cumplimiento de contratos como Contratos para Java . El mismo caso de uso que algo como AspectJ pero más nuevo y usa Anotaciones en lugar de archivos de configuración externos. Lo mejor de ambos trabajos de Aspectos y Afirmaciones.
1 es la solución ideal cuando se sabe que los datos entrantes son
null
necesitan y deben reemplazarse con algún valor predeterminado para que los consumidores no tengan que lidiar con todo el código repetitivo de verificación nula. La comprobación de los valores predeterminados conocidos también será más expresiva.2, 3 y 4 son solo generadores de excepciones alternativos convenientes para reemplazar
NullPointerException
por algo más informativo, que siempre es y mejora.En el final
null
en Java es casi en todos los casos un error lógico. Siempre debe esforzarse por eliminar la causa raíz deNullPointerExceptions
. Debe esforzarse por no utilizar lasnull
condiciones como lógica empresarial.if (x == null) { i = someDefault; }
simplemente realice la asignación inicial a esa instancia de objeto predeterminada.fuente
null
sea inesperado, entonces es un verdadero error, entonces todo debería detenerse por completo.null == null
(incluso en PHP), pero en las bases de datos,null != null
porque en las bases de datos representa un valor desconocido en lugar de "nada". Dos incógnitas no son necesariamente iguales, mientras que dos nada son iguales.Agregar comprobaciones nulas puede hacer que las pruebas sean problemáticas. Mira esta gran conferencia ...
Echa un vistazo a la conferencia de Google Tech: "Las conversaciones de código limpio: ¡no busques cosas!" habla de eso alrededor del minuto 24
http://www.youtube.com/watch?v=RlfLCWKxHJ0&list=PL693EFD059797C21E
La programación paranoica implica agregar cheques nulos en todas partes. Parece una buena idea al principio, sin embargo, desde una perspectiva de prueba, hace que sea difícil manejar su prueba de tipo de cheque nulo.
Además, cuando crea una condición previa para la existencia de algún objeto como
le impide crear House ya que lanzará una excepción. Suponga que sus casos de prueba están creando objetos simulados para probar algo que no sea Puerta, bueno, no puede hacer esto porque se requiere puerta
Los que han sufrido el infierno de la creación simulada son muy conscientes de este tipo de molestias.
En resumen, su conjunto de pruebas debe ser lo suficientemente robusto como para detectar puertas, casas, techos o lo que sea sin tener que ser paranoico al respecto. En serio, ¿qué tan difícil es agregar una prueba de verificación nula para objetos específicos en su prueba :)
Siempre debe preferir las aplicaciones que funcionan porque tiene varias pruebas que PRUEBAN que funciona, en lugar de ESPERAR que funcione simplemente porque tiene un montón de verificaciones nulas precondicionales en todo el lugar
fuente
tl; dr : es BUENO verificar
null
s inesperados pero MALO para que una aplicación intente hacerlos buenos.Detalles
Claramente, hay situaciones donde
null
es una entrada o salida válida para un método, y otras donde no lo es.Regla 1:
Regla # 2:
Dada una especificación clara del "contrato" de un método en relación con los visados
null
, es un error de programación pasar o devolver un lugarnull
donde no debe hacerlo.Regla # 3:
Si un método detecta un
null
que no debería estar allí, no debe intentar solucionar el problema convirtiéndolo en otra cosa. Eso solo oculta el problema del programador. En su lugar, debe permitir que ocurra el NPE y provocar un error para que el programador pueda descubrir cuál es la causa raíz y solucionarlo. Esperemos que la falla se note durante las pruebas. Si no, eso dice algo sobre su metodología de prueba.Regla # 4:
Si tiene errores en su código que resultan en muchos NPE, lo más difícil puede ser averiguar de dónde
null
provienen los valores. Una forma de facilitar el diagnóstico es escribir su código para quenull
se detecte lo antes posible. A menudo puede hacer esto junto con otros controles; p.ej(Obviamente, hay casos en los que las reglas 3 y 4 deben ser moderadas con la realidad. Por ejemplo (regla 3), algunos tipos de aplicaciones deben intentar continuar después de detectar probablemente errores de programación. Y (regla 4) puede haber demasiada comprobación de parámetros incorrectos. un impacto en el rendimiento)
fuente
Recomendaría arreglar el método para estar a la defensiva. Por ejemplo:
Debería estar más en la línea de esto:
Me doy cuenta de que esto es absolutamente trivial, pero si el invocador espera que un objeto le dé un objeto predeterminado que no hará que se pase un valor nulo.
fuente
new String()
en absoluto.null
s es la peor opción posible, peor que lanzar un NPE.Las siguientes reglas generales sobre nulo me han ayudado mucho hasta ahora:
Si los datos provienen de fuera de su control, verifique sistemáticamente los valores nulos y actúe de manera adecuada. Esto significa lanzar una excepción que tenga sentido para la función (marcada o desmarcada, solo asegúrese de que el nombre de la excepción le diga exactamente qué está sucediendo). Pero NUNCA no sea que pierda un valor en su sistema que pueda llevar sorpresas.
Si Null está dentro del dominio de valores apropiados para su modelo de datos, trátelo adecuadamente.
Cuando devuelva valores, intente no devolver valores nulos siempre que sea posible. Siempre prefiera Listas vacías, cadenas vacías, patrones de objetos nulos. Mantenga los valores nulos como valores devueltos cuando esta sea la mejor representación posible de datos para un caso de uso dado.
Probablemente el más importante de todos ... Pruebas, pruebas y prueba de nuevo. Cuando pruebe su código, no lo pruebe como un codificador, pruébelo como una dominatriz psicópata nazi e intente imaginar todo tipo de formas de torturar ese código.
Esto tiende un poco al lado paranoico con respecto a los nulos que a menudo conducen a fachadas y proxies que interconectan los sistemas con el mundo exterior y los valores estrictamente controlados en el interior con abundante redundancia. El mundo exterior aquí significa casi todo lo que no codifiqué. Lleva un tiempo de ejecución de costo, pero hasta ahora rara vez tuve que optimizar esto creando "secciones nulas seguras" de código. Sin embargo, debo decir que en su mayoría creo sistemas de larga duración para el cuidado de la salud y lo último que quiero es que el subsistema de interfaz que lleva sus alergias al yodo al escáner CT se bloquee debido a un puntero nulo inesperado porque alguien más en otro sistema nunca se dio cuenta de que los nombres podrían contener apóstrofes o caracteres como 但 耒耨。
de todos modos ... mis 2 centavos
fuente
Yo propondría usar el patrón Option / Some / None de lenguajes funcionales. No soy un especialista en Java, pero uso intensivamente mi propia implementación de este patrón en mi proyecto C #, y estoy seguro de que podría convertirse al mundo Java.
La idea detrás de este patrón es: si es lógico tener una situación en la que existe la posibilidad de ausencia de un valor (por ejemplo, cuando se recupera de la base de datos por id), se proporciona un objeto de tipo Opción [T], donde T es un valor posible . I caso de ausencia de un objeto de valor de la clase None [T] devuelto, en caso de que exista la existencia del valor - objeto de Some [T] devuelto, que contiene el valor.
En este caso, debe manejar la posibilidad de ausencia de valor, y si revisa el código, podría encontrar fácilmente un lugar de manejo incorrecto. Para obtener una inspiración de la implementación del lenguaje C #, consulte mi repositorio de bitbucket https://bitbucket.org/mikegirkin/optionsomenone
Si devuelve un valor nulo y es lógicamente equivalente a un error (por ejemplo, no existe un archivo o no se pudo conectar), debe lanzar una excepción o utilizar otro patrón de manejo de errores. La idea detrás de esto, de nuevo, termina con una solución, cuando debe manejar la situación de ausencia de valor, y podría encontrar fácilmente los lugares de manejo incorrecto en el código.
fuente
Bueno, si uno de los posibles resultados de su método es un valor nulo, entonces debe codificar defensivamente para eso, pero si se supone que un método devuelve un valor no nulo, pero no lo haría, lo arreglaría con seguridad.
Como de costumbre, depende del caso, como con la mayoría de las cosas en la vida :)
fuente
Las bibliotecas lang de Apache Commons proporcionan una forma de manejar valores nulos
El método defaultIfNull en la clase ObjectUtils le permite devolver un valor predeterminado si el objeto pasado es nulo
fuente
No use el tipo Opcional, a menos que realmente sea opcional, a menudo la salida se maneja mejor como una excepción, a menos que realmente esperara nulo como una opción, y no porque escriba código con errores regularmente.
El problema no es nulo como un tipo que tiene sus usos, como señala el artículo de Google. El problema es que los nulos deben verificarse y manejarse, a menudo se pueden salir con gracia.
Hay una serie de casos nulos que representan condiciones inválidas en áreas fuera de la operación de rutina del programa (entrada inválida del usuario, problemas de la base de datos, fallas de la red, archivos faltantes, datos corruptos), que es para lo que son las excepciones verificadas, manejarlas, incluso si es solo para iniciar sesión.
El manejo de excepciones permite diferentes prioridades y privilegios operativos en la JVM en lugar de un tipo, como Opcional, que tiene sentido por la naturaleza excepcional de su ocurrencia, incluido el soporte temprano para la carga diferida prioritaria del controlador en la memoria, ya que no Lo necesito dando vueltas todo el tiempo.
No necesita escribir controladores nulos en todo el lugar, solo donde es probable que ocurran, en cualquier lugar donde pueda acceder a un servicio de datos poco confiable, y dado que la mayoría de estos caen en pocos patrones generales que pueden resumirse fácilmente, realmente solo necesita una llamada de manejador, excepto en casos excepcionales.
Entonces, supongo que mi respuesta sería envolverlo en una excepción marcada y manejarlo, o corregir el código poco confiable si está dentro de su capacidad.
fuente