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 es necesario 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;
}
java
nullpointerexception
LiJiaming
fuente
fuente
ArgumentNullException
en casos como ese (en lugar deNullReferenceException
): en realidad es una muy buena pregunta de por qué plantearíasNullPointerException
explícitamente aquí (en lugar de una diferente).IllegalArgumentException
oNullPointerException
por un argumento nulo. La convención JDK es la última.Respuestas:
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 desperdiciar los 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 ninguna asignación, 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 documentación 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.
fuente
new NullPointerException(message)
constructor para aclarar qué es nulo. Bueno para personas que no tienen acceso a su código fuente. Incluso hicieron de esto una línea en JDK 8 con elObjects.requireNonNull(object, message)
método de utilidad.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 llamaba
hash(key)
egetNode(hash, key)
incluso cuando senull
había pasadoremappingFunction
antes de que se lanzara el NPE.Peor aún, si la
if
condición esfalse
entonces, tomamos laelse
rama, que no usaremappingFunction
en absoluto, lo que significa que el método no siempre arroja NPE cuandonull
se pasa a; si lo hace depende del estado del mapa.Ambos escenarios son malos. Si
null
no es un valor válido pararemappingFunction
el método, debe arrojar una excepción de manera consistente, independientemente del estado interno del objeto en el momento de la llamada, y debe hacerlo sin hacer un trabajo innecesario que no tiene sentido dado que solo se lanzará. Finalmente, es un buen principio de código limpio y claro contar con 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á.
fuente
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 tome el intérprete de Java y el compilador JIT 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
null
en el código evitaría el impacto en el rendimiento de 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
null
mensaje. (Android Java es diferente).fuente
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 plantear explícitamente una excepción en casos como este, pero es específicamente una
ArgumentNullException
, que es algo más específica. (La convención de C # es queNullReferenceException
siempre representa un error de algún tipo; simplemente, no debería suceder nunca en el código de producción; concedido,ArgumentNullException
por lo general también lo hace, pero podría ser un error más en la línea de "usted no entiendo cómo usar la biblioteca correctamente "tipo de error).Entonces, básicamente, en C #
NullReferenceException
significa que su programa realmente trató de usarlo, mientrasArgumentNullException
que significa que reconoció que el valor era incorrecto y ni siquiera se molestó en tratar de usarlo. Las implicaciones pueden ser realmente diferentes (dependiendo de las circunstancias) porqueArgumentNullException
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á planteando algo como
ArgumentNullException
oIllegalArgumentException
, eso es parte del punto de hacer la verificación: desea una excepción diferente de la que "normalmente" obtendría.De cualquier manera, aumentar explícitamente 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 lo verificó explícitamente
null
, no sé si es porque pensó que nadie pasaría unanull
discusión, está contando que arroje la excepción de todos modos, o simplemente se olvidó de verificar eso.fuente
null
objeto". No estoy loco por eso, pero ese parece ser el diseño previsto.NullReferenceException
(equivalente aNullPointerException
) significa que su código realmente intentó usarlo (que siempre es un error, nunca debería suceder en el código de producción), vs. "Sé que el argumento está mal, así que ni siquiera intenta usarlo ". También hayArgumentException
(lo que significa que el argumento fue incorrecto por alguna otra razón).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ó.
fuente
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 verificación no estuviera allí, deberá excluir la posibilidad de que haya algún error encomputeIfPresent()
sí mismo que provoque la excepción.Obviamente, lanzar el genérico
NullPointerException
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 un
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 se proporciona condición evaluar a falso. Entonces, si corristedebajo de un depurador, y algo pasó
NULL
a cualquiera de los argumentos, el programa se detendría justo en la línea que indica qué argumento eraNULL
, y usted podría examinar todas las variables locales de toda la pila de llamadas en el tiempo libre.fuente
assert something != null;
pero eso requiere la-assertions
bandera cuando se ejecuta la aplicación. Si la-assertions
bandera no está allí, la palabra clave de aserción no arrojará una Excepción de AfirmaciónEs porque es posible que no suceda naturalmente. Veamos un código como este:
(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
c
que no se utilizará realmente yNullPointerException
no se lanzará, incluso sic == 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
if (c == null) throw new NullPointerException()
es mejor.fuente
Es intencional proteger más daños o entrar en un estado inconsistente.
fuente
Además de todas las otras respuestas excelentes aquí, también me gustaría agregar algunos casos.
Puede agregar un mensaje si crea su propia excepción
Si arrojas el
NullPointerException
tuyo, puedes agregar un mensaje (¡que definitivamente deberías!)El mensaje predeterminado es
null
denew NullPointerException()
y todos los métodos que lo usan, por ejemploObjects.requireNonNull
. Si imprime ese nulo, incluso puede traducirse a una cadena vacía ...Un poco corto y poco informativo ...
El seguimiento de la pila dará mucha información, pero para que el usuario sepa qué era nulo, debe desenterrar el código y mirar la fila exacta.
Ahora imagine que NPE se envuelve y se envía a través de la red, por ejemplo, como un mensaje en un error del servicio web, tal vez entre diferentes departamentos o incluso organizaciones. En el peor de los casos, nadie puede descubrir qué
null
significa ...Las llamadas a métodos encadenados te mantendrán adivinando
Una excepción solo le indicará en qué fila se produjo la excepción. Considere la siguiente fila:
Si recibe un NPE y apunta en esta fila, uno de los cuales
repository
ysomeObject
fue nula?En cambio, al verificar estas variables cuando las obtiene, al menos apuntará a una fila donde, con suerte, son la única variable que se maneja. Y, como se mencionó anteriormente, aún mejor si su mensaje de error contiene el nombre de la variable o similar.
Los errores al procesar muchas entradas deben proporcionar información de identificación
Imagine que su programa está procesando un archivo de entrada con miles de filas y de repente hay una NullPointerException. Miras el lugar y te das cuenta de que alguna entrada fue incorrecta ... ¿qué entrada? Necesitará más información sobre el número de fila, tal vez la columna o incluso el texto de la fila completa para comprender qué fila en ese archivo necesita corrección.
fuente