todas tipos objeto nulo las excepciones ejemplos java nullpointerexception hashmap

objeto - tipos de excepciones en java



¿Por qué lanzar explícitamente una NullPointerException en lugar de dejar que ocurra naturalmente? (8)

Al leer el código fuente JDK, encuentro común que el autor verificará los parámetros si son nulos y luego lanzará una nueva NullPointerException () manualmente. ¿Por qué lo hacen? Creo que no hay necesidad de hacerlo, ya que arrojará una nueva NullPointerException () cuando llame a cualquier método. (Aquí hay un código fuente de HashMap, por ejemplo :)

public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { if (remappingFunction == null) throw new NullPointerException(); Node<K,V> e; V oldValue; int hash = hash(key); if ((e = getNode(hash, key)) != null && (oldValue = e.value) != null) { V v = remappingFunction.apply(key, oldValue); if (v != null) { e.value = v; afterNodeAccess(e); return v; } else removeNode(hash, key, null, false, true); } return null; }


Además de las razones enumeradas por la excelente respuesta de @ shmosel ...

Rendimiento: puede haber / ha habido beneficios de rendimiento (en algunas JVM) al lanzar el NPE explícitamente en lugar de dejar que la JVM lo haga.

Depende de la estrategia que el intérprete de Java y el compilador JIT adopten para detectar la desreferenciación de punteros nulos. Una estrategia es no probar nulo, sino atrapar el SIGSEGV que ocurre cuando una instrucción intenta acceder a la dirección 0. Este es el enfoque más rápido en el caso donde la referencia siempre es válida, pero es costosa en el caso de NPE.

Una prueba explícita de null en el código evitaría el impacto de rendimiento SIGSEGV en un escenario donde los NPE eran frecuentes.

(Dudo que sea una microoptimización que valga la pena en una JVM moderna, pero podría haber sido en el pasado).

Compatibilidad: la razón probable de que no haya ningún mensaje en la excepción es la compatibilidad con los NPE que arroja la propia JVM. En una implementación Java compatible, un NPE lanzado por la JVM tiene un mensaje null . (Android Java es diferente).


Además de lo que otras personas han señalado, vale la pena señalar el papel de la convención aquí. En C #, por ejemplo, también tiene la misma convención de generar explícitamente una excepción en casos como este, pero es específicamente una excepción ArgumentNullException , que es algo más específica. (La convención de C # es que NullReferenceException siempre representa un error de algún tipo; simplemente, no debería suceder nunca en el código de producción; concedido, ArgumentNullException generalmente también lo hace, pero podría ser un error más en la línea de "usted no no entiendo cómo usar la biblioteca correctamente "tipo de error).

Entonces, básicamente, en C # NullReferenceException significa que su programa realmente intentó usarlo, mientras que ArgumentNullException significa que reconoció que el valor era incorrecto y ni siquiera se molestó en intentar usarlo. Las implicaciones pueden ser diferentes (dependiendo de las circunstancias) porque ArgumentNullException significa que el método en cuestión aún no tuvo efectos secundarios (ya que falló las condiciones previas del método).

Por cierto, si está generando algo como ArgumentNullException o IllegalArgumentException , eso es parte del punto de hacer la verificación: desea una excepción diferente de la que "normalmente" obtendría.

De cualquier manera, el aumento explícito de la excepción refuerza la buena práctica de ser explícito sobre las condiciones previas y los argumentos esperados de su método, lo que hace que el código sea más fácil de leer, usar y mantener. Si no comprobaste explícitamente el null , no sé si es porque pensaste que nadie pasaría un argumento null , estás contando que arroja la excepción de todos modos, o simplemente olvidaste comprobarlo.


Convierte una condición de error aparentemente errática en una violación clara del contrato: la función tiene algunas condiciones previas para funcionar correctamente, por lo que las verifica de antemano y exige que se cumplan.

El efecto es que no tendrá que depurar computeIfPresent() cuando obtenga la excepción. Una vez que vea que la excepción proviene de la verificación de precondición, sabe que llamó a la función con un argumento ilegal. Si la comprobación no estuviera allí, deberá excluir la posibilidad de que haya algún error dentro de computeIfPresent() que conduzca a la excepción.

Obviamente, lanzar la NullPointerException genérica es una muy mala elección, ya que no indica una violación del contrato en sí misma. IllegalArgumentException sería una mejor opción.

Nota al margen:
No sé si Java permite esto (lo dudo), pero los programadores de C / C ++ usan una assert() en este caso, que es significativamente mejor para la depuración: le dice al programa que se bloquee de inmediato y lo más duro posible si el condición proporcionada evaluar a falso. Entonces, si corriste

void MyClass_foo(MyClass* me, int (*someFunction)(int)) { assert(me); assert(someFunction); ... }

debajo de un depurador, y algo pasaba NULL a cualquiera de los argumentos, el programa se detendría justo en la línea indicando qué argumento era NULL , y usted podría examinar todas las variables locales de la pila de llamadas completa en el tiempo libre.


Es así que obtendrá la excepción tan pronto como cometa el error, en lugar de más adelante cuando esté usando el mapa y no entienda por qué sucedió.


Es intencional proteger más daños o entrar en un estado inconsistente.


Es por claridad, consistencia y para evitar que se realice trabajo adicional e innecesario.

Considere lo que sucedería si no hubiera una cláusula de guardia en la parte superior del método. Siempre llamaría a hash(key) y getNode(hash, key) incluso cuando se hubiera pasado null para la función de remappingFunction antes de que se lanzara el NPE.

Peor aún, si la condición if es false , tomamos la rama else , que no utiliza la función remappingFunction en absoluto, lo que significa que el método no siempre arroja NPE cuando se pasa un null ; si lo hace depende del estado del mapa.

Ambos escenarios son malos. Si null no es un valor válido para la función de remappingFunction el método debería arrojar una excepción de manera consistente independientemente del estado interno del objeto en el momento de la llamada, y debería hacerlo sin hacer un trabajo innecesario que no tiene sentido dado que solo va a lanzar. Finalmente, es un buen principio de código limpio y claro tener la protección por adelantado para que cualquiera que revise el código fuente pueda ver fácilmente que lo hará.

Incluso si la excepción fuera lanzada actualmente por cada rama del código, es posible que una futura revisión del código cambie eso. Realizar la verificación al principio asegura que definitivamente se realizará.


Es porque es posible que no suceda naturalmente. Veamos un código como este:

bool isUserAMoron(User user) { Connection c = UnstableDatabase.getConnection(); if (user.name == "Moron") { // In this case we don''t need to connect to DB return true; } else { return c.makeMoronishCheck(user.id); } }

(por supuesto, hay numerosos problemas en esta muestra sobre la calidad del código. Perdón por la pereza de imaginar una muestra perfecta)

Situación en la que c no se utilizará realmente y no se lanzará NullPointerException incluso si c == null es posible.

En situaciones más complicadas, resulta muy difícil cazar esos casos. Esta es la razón por la cual la verificación general como if (c == null) throw new NullPointerException() es mejor.


Hay una serie de razones que me vienen a la mente, varias de ellas estrechamente relacionadas:

Fallo rápido: si va a fallar, lo mejor es fallar más temprano que tarde. Esto permite detectar los problemas más cerca de su origen, lo que facilita su identificación y recuperación. También evita el desperdicio de ciclos de CPU en el código que seguramente fallará.

Intención: Lanzar la excepción explícitamente deja en claro a los mantenedores que el error está allí a propósito y que el autor estaba al tanto de las consecuencias.

Consistencia: si se permitiera que el error ocurriera naturalmente, podría no ocurrir en todos los escenarios. Si no se encuentra ningún mapeo, por ejemplo, remappingFunction nunca se usaría y la excepción no se lanzaría. Validar la entrada por adelantado permite un comportamiento más determinista y una documentation más clara.

Estabilidad: el código evoluciona con el tiempo. El código que encuentra una excepción, naturalmente, podría, después de un poco de refactorización, dejar de hacerlo, o hacerlo en diferentes circunstancias. Lanzarlo explícitamente hace que sea menos probable que el comportamiento cambie inadvertidamente.