Tengo un método de establecimiento simple para una propiedad y null
no es apropiado para esta propiedad en particular. Siempre me he desgarrado en esta situación: ¿debería tirar un IllegalArgumentException
o un NullPointerException
? De los javadocs, ambos parecen apropiados. ¿Hay algún tipo de estándar entendido? ¿O es solo una de esas cosas que debes hacer lo que prefieras y ambas son realmente correctas?
547
IllegalArgumentException
está en contradicción con los Objects.requireNonNull (T) de Java y las Precondiciones de Guavas.checkNotNull (T) que arroja aNullPointerException
. Sin embargo, la respuesta correcta es definitivaIllegalArgumentException
como se explica en la excelente respuesta de Jason Cohen y su sección de comentarios .Debería usar
IllegalArgumentException
(IAE), noNullPointerException
(NPE) por las siguientes razones:Primero, el NPE JavaDoc enumera explícitamente los casos en los que el NPE es apropiado. Tenga en cuenta que todos ellos son lanzados por el tiempo de ejecución cuando
null
se usa de manera inapropiada. Por el contrario, el IAE JavaDoc no podría ser más claro: "Lanzado para indicar que un método ha pasado un argumento ilegal o inapropiado". Sí, ese eres tú!En segundo lugar, cuando ve un NPE en una traza de pila, ¿qué supone? Probablemente que alguien haya desreferenciado a
null
. Cuando vea IAE, asume que la persona que llama del método en la parte superior de la pila pasó un valor ilegal. Nuevamente, la última suposición es cierta, la primera es engañosa.En tercer lugar, dado que IAE está claramente diseñado para validar parámetros, debe asumirlo como la opción de excepción predeterminada, entonces, ¿por qué elegiría NPE en su lugar? Ciertamente no para un comportamiento diferente: ¿realmente espera que el código de llamada capture los NPE por separado del IAE y, como resultado, haga algo diferente? ¿Estás intentando comunicar un mensaje de error más específico? Pero puede hacerlo en el texto del mensaje de excepción de todos modos, como debería hacerlo para todos los demás parámetros incorrectos.
Cuarto, todos los demás datos de parámetros incorrectos serán IAE, entonces ¿por qué no ser consistentes? ¿Por qué es que un ilegal
null
es tan especial que merece una excepción separada de todos los otros tipos de argumentos ilegales?Finalmente, acepto el argumento dado por otras respuestas de que partes de la API de Java usan NPE de esta manera. Sin embargo, la API de Java es inconsistente con todo, desde los tipos de excepción hasta las convenciones de nomenclatura, por lo que creo que copiar ciegamente (su parte favorita de) la API de Java no es un argumento lo suficientemente bueno como para superar estas otras consideraciones.
fuente
Validate.notNull
(commons lang) yPreconditions.checkNotNull
(guava) arrojan NPE :-(checkArgument(arg != null)
, sin la conveniencia de que devuelva arg, o puede crear una utilidad local para su proyecto ". code.google.com/p/guava-libraries/wiki/IdeaGraveyardEl estándar es lanzar el
NullPointerException
. El "Java efectivo" generalmente infalible analiza esto brevemente en el Ítem 42 (primera edición), el Ítem 60 (segunda edición) o el Ítem 72 (tercera edición) "Favorecer el uso de excepciones estándar":fuente
Estaba totalmente a favor de lanzar
IllegalArgumentException
parámetros nulos, hasta hoy, cuando noté eljava.util.Objects.requireNonNull
método en Java 7. Con ese método, en lugar de hacerlo:tu puedes hacer:
y arrojará un
NullPointerException
si el parámetro que pasa esnull
.Dado que ese método es justo en el medio, considero
java.util
que su existencia es una indicación bastante fuerte de que lanzarNullPointerException
es "la forma Java de hacer las cosas".Creo que estoy decidido de todos modos.
Tenga en cuenta que los argumentos sobre la depuración dura son falsos porque, por supuesto, puede proporcionar un mensaje para
NullPointerException
decir qué fue nulo y por qué no debería ser nulo. Al igual que conIllegalArgumentException
.Una ventaja adicional de esto
NullPointerException
es que, en un código crítico de alto rendimiento, puede prescindir de una comprobación explícita de nulo (y unNullPointerException
mensaje de error amigable), y solo confiar enNullPointerException
que obtendrá automáticamente cuando llame a un método en nulo parámetro. Siempre que llame a un método rápidamente (es decir, falle rápidamente), tendrá esencialmente el mismo efecto, pero no tan fácil de usar para el desarrollador. La mayoría de las veces probablemente sea mejor verificar explícitamente y lanzar un mensaje útil para indicar qué parámetro era nulo, pero es bueno tener la opción de cambiar eso si el rendimiento lo dicta sin romper el contrato publicado del método / constructor.fuente
Preconditions.checkNotNull(arg)
también arroja NPE.Tiendo a seguir el diseño de bibliotecas JDK, especialmente Colecciones y Concurrencia (Joshua Bloch, Doug Lea, esos tipos saben cómo diseñar API sólidas). De todos modos, muchas API en el JDK lanzan proactivamente
NullPointerException
.Por ejemplo, el Javadoc para
Map.containsKey
estados:Es perfectamente válido lanzar tu propio NPE. La convención es incluir el nombre del parámetro que era nulo en el mensaje de la excepción.
El patrón dice:
Hagas lo que hagas, no permitas que se establezca un valor incorrecto y lanza una excepción más tarde cuando otro código intente usarlo. Eso hace que la depuración sea una pesadilla. Siempre debe seguir el principio de "falla rápida".
fuente
Votó el argumento de Jason Cohen porque estaba bien presentado. Déjame desmembrarlo paso a paso. ;-)
El NPE JavaDoc dice explícitamente, "otros usos ilegales del objeto nulo" . Si solo se limitara a situaciones en las que el tiempo de ejecución encuentra un valor nulo cuando no debería, todos estos casos podrían definirse de manera mucho más sucinta.
No puede evitarlo si asume lo incorrecto, pero suponiendo que la encapsulación se aplique correctamente, realmente no debería importarle o notar si un nulo fue desreferenciado de manera inapropiada frente a si un método detectó un nulo inapropiado y activó una excepción.
Elegiría NPE sobre IAE por múltiples razones
En realidad, otros argumentos inválidos pueden dar lugar a todo tipo de otras excepciones. UnknownHostException , FileNotFoundException , una variedad de excepciones de error de sintaxis, IndexOutOfBoundsException , fallas de autenticación, etc., etc.
En general, siento que NPE está muy difamado porque tradicionalmente se ha asociado con código que no sigue el principio de falla rápida . Eso, más el hecho de que el JDK no haya llenado los NPE con una cadena de mensajes realmente ha creado un fuerte sentimiento negativo que no está bien fundado. De hecho, la diferencia entre NPE e IAE desde una perspectiva de tiempo de ejecución es estrictamente el nombre. Desde esa perspectiva, cuanto más preciso sea con el nombre, más claridad le dará a la persona que llama.
fuente
Es una pregunta de estilo de "Guerra Santa". En otras palabras, ambas alternativas son buenas, pero las personas tendrán sus preferencias que defenderán hasta la muerte.
fuente
NullPointerException
debería lanzarse: es la convención que utiliza el JDK, y requiere para las interfaces, es más específica (comoIndexOutOfBoundsException
, etc.), etc.Si es un
setter
método ynull
se le está pasando, creo que tendría más sentido lanzar unIllegalArgumentException
. ANullPointerException
parece tener más sentido en el caso en el que intenta utilizar realmente elnull
.Por lo tanto, si usted está usando y que es
null
,NullPointer
. Si es que se pasa en y esnull
,IllegalArgument
.fuente
Apache Commons Lang tiene una NullArgumentException que hace varias de las cosas discutidas aquí: extiende IllegalArgumentException y su único constructor toma el nombre del argumento que debería haber sido no nulo.
Si bien siento que lanzar algo como NullArgumentException o IllegalArgumentException describe con mayor precisión las circunstancias excepcionales, mis colegas y yo hemos optado por diferir los consejos de Bloch sobre el tema.
fuente
No podría estar más de acuerdo con lo que se dice. Falla temprano, falla rápido. Bastante bueno Excepción mantra.
La pregunta sobre qué excepción lanzar es principalmente una cuestión de gusto personal. En mi opinión, IllegalArgumentException parece más específico que usar un NPE, ya que me dice que el problema fue con un argumento que pasé al método y no con un valor que se pudo haber generado al realizar el método.
Mis 2 centavos
fuente
En realidad, la cuestión de lanzar IllegalArgumentException o NullPointerException es, en mi humilde opinión, solo una "guerra santa" para una minoría con una comprensión incompleta del manejo de excepciones en Java. En general, las reglas son simples y son las siguientes:
Existen al menos tres muy buenas razones contra el caso de mapear todo tipo de violaciones de restricciones de argumentos a IllegalArgumentException, y la tercera probablemente sea tan grave como para marcar el mal estilo de la práctica:
(1) Un programador no puede suponer con seguridad que todos los casos de violaciones de restricciones de argumentos dan como resultado IllegalArgumentException, porque la gran mayoría de las clases estándar usan esta excepción en lugar de una papelera si no hay más tipos específicos de excepciones disponibles. Intentar asignar todos los casos de violaciones de restricciones de argumentos a IllegalArgumentException en su API solo conduce a la frustración del programador al usar sus clases, ya que las bibliotecas estándar en su mayoría siguen diferentes reglas que violan las suyas, ¡y la mayoría de los usuarios de su API también las usarán!
(2) El mapeo de las excepciones en realidad da como resultado un tipo diferente de anomalía, causada por una herencia única: todas las excepciones de Java son clases y, por lo tanto, solo admiten herencia única. Por lo tanto, no hay forma de crear una excepción que realmente sea una NullPointerException y una IllegalArgumentException, ya que las subclases solo pueden heredar de una u otra. Lanzar una excepción IllegalArgumentException en caso de un argumento nulo, por lo tanto, hace que sea más difícil para los usuarios de API distinguir entre problemas cada vez que un programa intenta corregir el problema mediante programación, por ejemplo, introduciendo valores predeterminados en una repetición de llamada.
(3) El mapeo en realidad crea el peligro de enmascarar errores: para mapear las violaciones de restricciones de argumentos en IllegalArgumentException, deberá codificar un try-catch externo dentro de cada método que tenga argumentos restringidos. Sin embargo, simplemente atrapar RuntimeException en este bloque de captura está fuera de discusión, porque eso corre el riesgo de mapear RuntimeExceptions documentadas generadas por los métodos de libery utilizados dentro de los suyos en IllegalArgumentException, incluso si no son causados por violaciones de restricciones de argumentos. Por lo tanto, debe ser muy específico, pero incluso ese esfuerzo no lo protege del caso de que asigne accidentalmente una excepción de tiempo de ejecución no documentada de otra API (es decir, un error) en una IllegalArgumentException de su API.
Por otro lado, con la práctica estándar, las reglas siguen siendo simples y las causas de excepción permanecen desenmascaradas y específicas. Para el llamador de métodos, las reglas también son fáciles: - si encuentra una excepción de tiempo de ejecución documentada de cualquier tipo porque pasó un valor ilegal, repita la llamada con un valor predeterminado (para estas excepciones específicas son necesarias) o corrija su código - por otro lado, si encuentra una excepción de tiempo de ejecución que no está documentada para un conjunto dado de argumentos, presente un informe de error a los creadores del método para asegurarse de que se corrija su código o su documentación.
fuente
La práctica aceptada es usar IllegalArgumentException (mensaje de cadena) para declarar que un parámetro no es válido y dar la mayor cantidad de detalles posible ... Por lo tanto, para decir que se encontró que un parámetro era nulo, mientras que la excepción no era nula, haría algo Me gusta esto:
Prácticamente no tiene ninguna razón para usar implícitamente la "NullPointerException". NullPointerException es una excepción lanzada por la máquina virtual Java cuando intenta ejecutar código en una referencia nula (Like toString () ).
fuente
Lanzar una excepción que sea exclusiva de los
null
argumentos (yaNullPointerException
sea un tipo personalizado) hace que lasnull
pruebas automatizadas sean más confiables. Esta prueba automatizada se puede hacer con reflexión y un conjunto de valores predeterminados, como en Guava 'sNullPointerTester
. Por ejemplo,NullPointerTester
intentaría llamar al siguiente método ...... con dos listas de argumentos:
"", null
ynull, ImmutableList.of()
. Probaría que cada una de estas llamadas arroja lo esperadoNullPointerException
. Para esta implementación, pasar unanull
lista no produceNullPointerException
. Sin embargo, sucede que produce unIllegalArgumentException
porqueNullPointerTester
usa una cadena predeterminada de""
. SiNullPointerTester
espera sóloNullPointerException
paranull
los valores, que atrapa el fallo. Si esperaIllegalArgumentException
, lo echa de menos.fuente
Algunas colecciones suponen que
null
se rechaza utilizando enNullPointerException
lugar deIllegalArgumentException
. Por ejemplo, si compara un conjunto que contienenull
un conjunto que rechazanull
, el primer conjunto llamarácontainsAll
al otro y capturará suNullPointerException
- pero noIllegalArgumentException
. (Estoy mirando la implementación deAbstractSet.equals
).Podría argumentar razonablemente que el uso de excepciones no verificadas de esta manera es un antipatrón, que comparar colecciones que contienen
null
con colecciones que no pueden contenernull
es un error probable que realmente debería producir una excepción, o que ponernull
una colección es una mala idea . Sin embargo, a menos que esté dispuesto a decir queequals
debería arrojar una excepción en tal caso, está atrapado recordando queNullPointerException
se requiere en ciertas circunstancias, pero no en otras. ("IAE antes de NPE excepto después de 'c' ...")fuente
new TreeSet<>().containsAll(Arrays.asList((Object) null));
tiraNPE
porqueList
contienenull
.Como pregunta subjetiva, esto debería estar cerrado, pero como todavía está abierto:
Esto es parte de la política interna utilizada en mi lugar de empleo anterior y funcionó muy bien. Todo esto es de memoria, así que no puedo recordar la redacción exacta. Vale la pena señalar que no utilizaron excepciones marcadas, pero eso está más allá del alcance de la pregunta. Las excepciones no comprobadas que usaron se clasificaron en 3 categorías principales.
NullPointerException: no lance intencionalmente. Los NPE solo deben ser lanzados por la VM cuando se desreferencia una referencia nula. Se deben hacer todos los esfuerzos posibles para garantizar que nunca se arrojen. @Nullable y @NotNull deben usarse junto con herramientas de análisis de código para encontrar estos errores.
IllegalArgumentException: se produce cuando un argumento de una función no se ajusta a la documentación pública, de modo que el error puede identificarse y describirse en términos de los argumentos transmitidos. La situación del OP entraría en esta categoría.
IllegalStateException: se produce cuando se llama a una función y sus argumentos son inesperados en el momento en que se pasan o incompatibles con el estado del objeto del que es miembro el método.
Por ejemplo, había dos versiones internas de IndexOutOfBoundsException usadas en cosas que tenían una longitud. Uno es una subclase de IllegalStateException, utilizada si el índice era mayor que la longitud. La otra, una subclase de IllegalArgumentException, utilizada si el índice era negativo. Esto se debía a que podía agregar más elementos al objeto y el argumento sería válido, mientras que un número negativo nunca es válido.
Como dije, este sistema funciona realmente bien, y le tomó a alguien explicar por qué la distinción existe: "Dependiendo del tipo de error, es bastante sencillo para usted averiguar qué hacer. Incluso si no puede realmente imaginar para averiguar qué salió mal, puede averiguar dónde detectar ese error y crear información de depuración adicional ".
NullPointerException: maneje el caso nulo o ponga una aserción para que no se arroje el NPE. Si pones una afirmación es solo uno de los otros dos tipos. Si es posible, continúe la depuración como si la aserción estuviera allí en primer lugar.
IllegalArgumentException: tienes algo mal en tu sitio de llamadas. Si los valores que se pasan son de otra función, averigüe por qué recibe un valor incorrecto. Si está pasando uno de sus argumentos, el error verifica la pila de llamadas hasta que encuentre la función que no devuelve lo que esperaba.
IllegalStateException: no ha llamado a sus funciones en el orden correcto. Si está utilizando uno de sus argumentos, verifíquelos y arroje una IllegalArgumentException que describa el problema. Luego puede propagar las mejillas contra la pila hasta que encuentre el problema.
De todos modos, su punto era que solo puedes copiar las IllegalArgumentAssertions en la pila. No hay forma de propagar IllegalStateExceptions o NullPointerExceptions en la pila porque tienen algo que ver con su función.
fuente
En general, un desarrollador nunca debe lanzar una NullPointerException. El tiempo de ejecución genera esta excepción cuando el código intenta desreferenciar una variable cuyo valor es nulo. Por lo tanto, si su método quiere rechazar explícitamente nulo, en lugar de que solo un valor nulo genere una NullPointerException, debe lanzar una IllegalArgumentException.
fuente
Quería destacar los argumentos nulos de otros argumentos ilegales, por lo que obtuve una excepción de IAE llamada NullArgumentException. Sin siquiera tener que leer el mensaje de excepción, sé que se pasó un argumento nulo a un método y al leer el mensaje, descubro qué argumento era nulo. Todavía capturo la NullArgumentException con un controlador IAE, pero en mis registros es donde puedo ver la diferencia rápidamente.
fuente
la dicotomía ... ¿No se superponen? Solo las partes no superpuestas de un todo pueden hacer una dicotomía. Como yo lo veo:
fuente
NullPointerException
no haría nada. Lo único que podría ayudar esIllegalNullPointerArgumentException extends IllegalArgumentException, NullPointerException
, pero no tenemos herencia múltiple.NullPointerException
arrojado al intentar acceder a un objeto con una variable de referencia cuyo valor actual esnull
.IllegalArgumentException
arrojado cuando un método recibe un argumento con un formato diferente al esperado.fuente
Según su escenario,
IllegalArgumentException
es la mejor elección, porquenull
no es un valor válido para su propiedad.fuente
Idealmente, no se deben lanzar excepciones de tiempo de ejecución. Se debe crear una excepción marcada (excepción comercial) para su escenario. Porque si alguna de estas excepciones se inicia y registra, desvía al desarrollador mientras revisa los registros. En cambio, las excepciones comerciales no crean ese pánico y generalmente se ignoran al resolver problemas en los registros.
fuente
Las definiciones de los enlaces a las dos excepciones anteriores son IllegalArgumentException: se arroja para indicar que un método ha pasado un argumento ilegal o inapropiado. NullPointerException: se produce cuando una aplicación intenta usar nulo en un caso donde se requiere un objeto.
La gran diferencia aquí es que se supone que se usa IllegalArgumentException cuando se verifica que un argumento de un método es válido. Se supone que NullPointerException se usa siempre que un objeto se "usa" cuando es nulo.
Espero que eso ayude a poner a los dos en perspectiva.
fuente
Si se trata de un "setter", o en algún lugar que voy a obtener un miembro para usar más tarde, tiendo a usar IllegalArgumentException.
Si es algo que voy a usar (desreferenciar) ahora mismo en el método, lanzo una NullPointerException de forma proactiva. Me gusta más que dejar que el tiempo de ejecución lo haga, porque puedo proporcionar un mensaje útil (parece que el tiempo de ejecución también podría hacerlo, pero eso es una queja para otro día).
Si estoy anulando un método, uso lo que use el método anulado.
fuente
Debería lanzar una IllegalArgumentException, ya que le resultará obvio al programador que ha hecho algo no válido. Los desarrolladores están tan acostumbrados a ver NPE lanzada por la VM, que cualquier programador no se daría cuenta de inmediato de su error y comenzaría a mirar al azar, o peor aún, culparía a su código de 'buggy'.
fuente
En este caso, IllegalArgumentException transmite información clara al usuario que usa su API que "no debe ser nulo". Como señalaron otros usuarios del foro, puede usar NPE si lo desea, siempre que transmita la información correcta al usuario que usa su API.
GaryF y tweakt descartaron las referencias de "Java efectivo" (que juro) que recomiendan usar NPE. Y observar cómo se construyen otras API buenas es la mejor manera de ver cómo construir su API.
Otro buen ejemplo es mirar las API de Spring. Por ejemplo, org.springframework.beans.BeanUtils.instantiateClass (Constructor ctor, Object [] args) tiene una línea Assert.notNull (ctor, "El constructor no debe ser nulo"). El método org.springframework.util.Assert.notNull (objeto de objeto, mensaje de cadena) verifica si el argumento (objeto) pasado es nulo y si lo es, arroja una nueva IllegalArgumentException (mensaje) que luego queda atrapada en la organización. springframework.beans.BeanUtils.instantiateClass (...) método.
fuente
Si elige lanzar un NPE y está utilizando el argumento en su método, puede ser redundante y costoso verificar explícitamente un nulo. Creo que la VM ya lo hace por ti.
fuente
NPE
y los programadores hablenIAE
antes de la VM si así lo desean.