¿Capturar una excepción de puntero nulo es un olor a código?

Recientemente, un compañero de trabajo mío escribió en algún código para capturar una excepción de puntero nulo alrededor de un método completo, y devolver un solo resultado. Señalé cómo podría haber habido una serie de razones para el puntero nulo, por lo que lo cambiamos a un control defensivo por el único resultado.

Sin embargo, la captura de NullPointerException me pareció incorrecta. En mi opinión, las excepciones de puntero nulo son el resultado de un código incorrecto y no son una excepción esperada en el sistema.

¿Hay algún caso en el que tenga sentido capturar una excepción de puntero nulo?

Sí, capturar cualquier RuntimeException es casi siempre un olor a código. El C2 Wiki parece estar de acuerdo.

Una excepción sería probablemente algunas piezas de código especialmente defensivas que ejecutan código bastante aleatorio de otros módulos. Ejemplos de tales estructuras defensivas serían EDT , ThreadPools / Executors y el sistema de complementos.

Puedo pensar exactamente en un uso para capturar una NullPointerException :

 catch (NullPointerException) { ApplyPainfulElectricShockToProgrammer(); } 

He tenido que atrapar excepciones nullpointer a veces debido a un error en la biblioteca de terceros. La biblioteca que usamos arrojó esa excepción, y no podíamos hacer nada al respecto.

En ese caso, está bien atraparlo, de lo contrario no.

Depende.

¿Cuán experimentado es este compañero de trabajo? ¿Está haciendo esto por ignorancia / pereza o hay una buena razón para eso? (¿Como este es el hilo principal sobre todo lo demás y nunca debería morir?)

El 90% de las veces que atrapa una excepción en tiempo de ejecución es incorrecto, el 99% atrapa una excepción NullPointerException está equivocada (si la razón es “Recibí muchas de ellas …” entonces todo el progtwigdor está equivocado y debes buscar cuidar el rest del código que está haciendo)

Pero en algunas circunstancias, la captura de una NullPointerException puede ser aceptable.

En general, creo que es un olor a código; me parece que los controles de defensa son mejores. Extendería eso para cubrir la mayoría de las excepciones no verificadas, excepto en los bucles de eventos, etc. que quieran detectar todos los errores para informar / registrar.

La excepción que se me ocurre es una llamada a una biblioteca que no se puede modificar y que puede generar una excepción de puntero nulo en respuesta a alguna falla de aserción que es difícil de comprobar proactivamente.

Gracioso

Acabo de encontrar algo que no debería hacerse en el trabajo:

 public static boolean isValidDate(final String stringDateValue) { String exp = "^[0-9]{2}/[0-9]{2}/[0-9]{4}$"; boolean isValid = false; try { if (Pattern.matches(exp, stringDateValue)) { String[] dateArray = stringDateValue.split("/"); if (dateArray.length == 3) { GregorianCalendar gregorianCalendar = new GregorianCalendar(); int annee = new Integer(dateArray[2]).intValue(); int mois = new Integer(dateArray[1]).intValue(); int jour = new Integer(dateArray[0]).intValue(); gregorianCalendar = new GregorianCalendar(annee, mois - 1, jour); gregorianCalendar.setLenient(false); gregorianCalendar.get(GregorianCalendar.YEAR); gregorianCalendar.get(GregorianCalendar.MONTH); gregorianCalendar.get(GregorianCalendar.DAY_OF_MONTH); isValid = true; } } } catch (Exception e) { isValid = false; } return isValid; } 

baaad 🙂

El desarrollador quería que el calendario planteara excepciones de este tipo:

 java.lang.IllegalArgumentException: DAY_OF_MONTH at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2316) at java.util.Calendar.updateTime(Calendar.java:2260) at java.util.Calendar.complete(Calendar.java:1305) at java.util.Calendar.get(Calendar.java:1088) 

para invalidar los valores …

Sí, funciona, pero no es una buena práctica …

La excepción de aumento (en particular, el llenado del seguimiento de la stack) cuesta mucho más que simplemente verificar datos manualmente sin una excepción …

Es malo, pero podría producir bytecode optimizado.

Si Integer i no es null mayor parte del tiempo, entonces el control disminuye el rendimiento general. El cheque en sí cuesta 3 instrucciones (0-4). El caso completo luego toma 7 instrucciones (0-14).

 public class IfNotNull { public Integer i; public String getIAsString() { if (i != null) { return i.toString(); } else { return ""; } } } public java.lang.String getIAsString(); Code: 0: aload_0 1: getfield #2 // Field i:Ljava/lang/Integer; 4: ifnull 15 7: aload_0 8: getfield #2 // Field i:Ljava/lang/Integer; 11: invokevirtual #3 // Method java/lang/Integer.toString:()Ljava/lang/String; 14: areturn // <- here we go 15: ldc #4 // String 17: areturn 

Siguiendo el enfoque de EAFP que es común en el mundo de Python. El caso null será costoso, pero solo necesitamos 4 instrucciones (0-7) para el caso not null .

 public class TryCatch { public Integer i; public String getIAsString() { try { return i.toString(); } catch (NullPointerException npe) { return ""; } } } public java.lang.String getIAsString(); Code: 0: aload_0 1: getfield #2 // Field i:Ljava/lang/Integer; 4: invokevirtual #3 // Method java/lang/Integer.toString:()Ljava/lang/String; 7: areturn // <- here we go 8: astore_1 9: ldc #5 // String a 11: areturn Exception table: from to target type 0 7 8 Class java/lang/NullPointerException 

¿Quién sabe si el comstackdor JIT puede optimizar esto?

Ciertamente es

La mayoría de las veces, sus variables no deberían ser nulas para comenzar. Muchos nuevos lenguajes están surgiendo con soporte integrado para tipos de referencia no anulables, es decir, tipos que nunca serán nulos.

Para los momentos en que se permita que su valor entrante sea nulo, debe hacer una comprobación. Pero las excepciones son definitivamente una mala forma de hacer esto.

Una instrucción if toma quizás tres instrucciones para realizar y es una verificación local (es decir, usted realiza la verificación en el mismo lugar donde necesita la garantía).

El uso de una excepción, por otro lado, puede requerir muchas más instrucciones: el sistema intenta buscar el método, falla, examina la tabla de excepciones para el manejador de excepciones apropiado, salta allí, ejecuta el controlador y salta de nuevo. Además, el cheque es potencialmente no local. Si su código es algo como esto:

 try return contacts.find("Mom").getEmail() catch (NullPointerException e) return null 

Usted no sabe si el NPE fue arrojado en ‘getEmail’ o en ‘find’.

¿Una solución técnica peor a un patrón muy, muy común, escrito de una manera más ofuscada? No es rango, pero definitivamente huele mal: /

El único lugar donde debe atrapar una NullPointerException (o específicamente, cualquier Throwable) se encuentra en algún nivel superior o límite del sistema para que su progtwig no se bloquee completamente y pueda recuperarse. Por ejemplo, la configuración de una página de error en su web.xml proporciona un resumen para que una aplicación web pueda recuperarse de una excepción y notificar al usuario.

La captura de una excepción de puntero NULL realmente depende del contexto … uno debe esforzarse por evitar reglas absolutas estrictas … las reglas deben aplicarse en contexto – quiere atrapar esta excepción y poner todo el software en un estado ESTABLE – sin hacer nada o casi casi nada. Todas esas reglas de encoding deben ser bien entendidas

En este punto, luego mira el software AUDIT TRACE … que deberías estar haciendo y descubre el ORIGEN de esta excepción.

La idea de que una Excepción de puntero NULL nunca se produzca debe ser verificable. Primero haga un análisis estático … (que es más difícil si entran los códigos / componentes de terceros) y luego realice una búsqueda exhaustiva del espacio de estado utilizando herramientas relevantes.

X

La captura de NPE (de hecho, cualquier RTE) puede ser necesaria para terminar de manera limpia una aplicación basada en la GUI de Swing.

editar: en este caso, generalmente se realiza a través de UncaughtExceptionHandler.

¿Qué tal esto?

 try { foo.x = bar; } catch (NullPointerException e) { // do whatever needs to be done } 

como una micro-optimización cuando foo puede ser nulo, pero casi nunca lo es?

La idea es esta:

  • Una verificación NULL explícita toma una sola instrucción de máquina

  • Por otro lado, la comprobación NULL en la segunda versión se puede hacer permitiendo el acceso NULL, capturando el SIGSEGV y lanzando una NullPointerException. Esto es gratis si el objeto no es NULL.

Hace mucho tiempo tuve un uso. Una biblioteca particularmente estúpida arrojaría NullPointerException cuando se le pidiera un objeto en una colección por clave y no se encontrara el objeto. No había otra manera de mirar hacia arriba que con la tecla y no hay forma de verificar si el objeto existía.

Algún tiempo después, iniciamos el proveedor y comenzamos a modificar la biblioteca. Ahora la biblioteca arroja una mejor excepción (mi cambio) y tiene una función de verificación (cambio de otra persona).

Por supuesto, siempre terminaría con exactamente una línea dentro del bloque try. Más y yo mismo sería culpable de código incorrecto.

Intento garantizar los resultados de mis interfaces, pero si algún código de biblioteca o de alguien puede producir nulo como resultado y estoy esperando una garantía de captura, podría ser viable. Por supuesto, lo que hagas una vez que lo atrapes depende de ti. A veces simplemente no tiene sentido comprobar si es nulo, y si lo captas tienes algún otro método para resolver el problema que podría no ser tan bueno pero que hace el trabajo.

Lo que estoy diciendo es excepciones de uso para lo que pueda, es una característica de lenguaje bastante bueno.

La captura de una NullPointerException puede ser útil si su método llama a una interfaz externa (o una API de SOAP) y existe la posibilidad de que el valor devuelto sea nulo. Aparte de eso, no hay un gran beneficio para atrapar estas excepciones.

Realmente depende de la definición de la interfaz. El manejo no estructurado de NPE es tan malo como atrapar Exception o Throwable.

Los nulos son útiles para identificar un estado no inicializado en lugar de usar una cadena vacía o max_int o lo que sea. Una vez que el lugar donde uso null regularmente está en lugares donde un objeto de callback no es relevante.

Me gusta mucho la anotación @Nullable proporcionada por Guice.

http://code.google.com/docreader/#p=google-guice&s=google-guice&t=UseNullable

Para eliminar NullPointerExceptions en su base de código, debe ser disciplinado sobre referencias nulas. Hemos tenido éxito al seguir y aplicar una regla simple:

Cada parámetro no es nulo a menos que se especifique explícitamente. La biblioteca de Google Collections y JSR-305 tienen API simples para obtener un valor nulo bajo control. Preconditions.checkNotNull se puede usar para fallar rápidamente si se encuentra una referencia nula, y @Nullable se puede usar para anotar un parámetro que permita el valor nulo.

Guice prohíbe nulo por defecto. Se negará a inyectar nulo, en su defecto con una ProvisionException. Si su clase permite el nulo, puede anotar el campo o parámetro con @Nullable. Guice reconoce cualquier anotación @Nullable, como edu.umd.cs.findbugs.annotations.Nullable o javax.annotation.Nullable.

Sí, en Java hay una necesidad de buscar una NullPointerException.

Lanzado cuando una aplicación intenta usar null en un caso donde se requiere un objeto. Éstas incluyen:

Llamar al método de instancia de un objeto nulo. Accediendo o modificando el campo de un objeto nulo. Tomando la longitud de nulo como si fuera una matriz. Accediendo o modificando las ranuras de nulo como si fuera una matriz. Lanzando nulo como si fuera un valor Throwable.

Las aplicaciones deben arrojar instancias de esta clase para indicar otros usos ilegales del objeto nulo.

NullPointerException en otros idiomas al leer archivos de texto (es decir, XML), cuyos registros no han sido validados con el caracter ASCII correcto y el formato de registro.

Si el progtwigdor es principiante, puede tener la costumbre de atrapar todas las excepciones que le impidan obtener el resultado final. Esto no debe ser entretenido por los revisores de código.

Capturar cualquier RuntimeException es malo. Pero si es realmente necesario, un comentario en el código será realmente útil para los futuros progtwigdores que trabajarán en ese código. Si no puede escribir un comentario razonable para atraparlos, debe evitarlos. período.