IllegalArgumentException o NullPointerException para un parámetro nulo?

Tengo un método de configuración simple para una propiedad y null no es apropiado para esta propiedad en particular. Siempre me he sentido desgarrado en esta situación: ¿debería arrojar una IllegalArgumentException , o una NullPointerException ? De los javadocs, ambos parecen apropiados. ¿Hay algún tipo de estándar entendido? ¿O es solo una de esas cosas en las que deberías hacer lo que prefieras y ambas son realmente correctas?

Parece que se IllegalArgumentException una IllegalArgumentException si no desea que null sea ​​un valor permitido, y la NullPointerException se lanzaría si estuviera intentando utilizar una variable que resultara null .

Debería utilizar IllegalArgumentException (IAE), no NullPointerException (NPE) por los siguientes motivos:

En primer lugar, el NPE JavaDoc enumera explícitamente los casos donde NPE es apropiado. Tenga en cuenta que todos ellos son lanzados por el tiempo de ejecución cuando null se usa de forma 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 un rastro de stack, ¿qué asume? Probablemente, alguien desreferenció un null . Cuando ve IAE, asume que la persona que llama del método en la parte superior de la stack pasó con un valor ilegal. Nuevamente, la última suposición es verdadera, la primera es engañosa.

En tercer lugar, dado que el IAE está claramente diseñado para validar parámetros, debe suponerlo como la opción predeterminada de excepción, entonces, ¿por qué elegiría NPE? Ciertamente, no por un comportamiento diferente: ¿de verdad espera que el código de llamada capte los NPE por separado del IAE y haga algo diferente como resultado? ¿Estás tratando de comunicar un mensaje de error más específico? Pero puede hacer eso en el texto del mensaje de excepción de todas formas, como debería hacer con todos los demás parámetros incorrectos.

En cuarto lugar, todos los demás datos de parámetros incorrectos serán IAE, ¿por qué no ser coherentes? ¿Por qué es que un null ilegal 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 no es coherente con todo, desde los tipos de excepción hasta las convenciones de nombres, así que creo que simplemente copiando ciegamente (su parte favorita de) la API de Java no es un argumento lo suficientemente bueno para superar estas otras consideraciones.

El estándar es arrojar la NullPointerException. La “Java Efectiva” generalmente infalible analiza esto brevemente en el Ítem 42 (primera edición), Ítem 60 (segunda edición), o el Ítem 72 (tercera edición) “Favorecer el uso de excepciones estándar”:

“Podría decirse que todas las invocaciones de métodos erróneos se reducen a un argumento ilegal o estado ilegal, pero otras excepciones se usan de manera estándar para ciertos tipos de argumentos y estados ilegales. Si un llamador pasa nulo en algún parámetro para el que se prohíben los valores nulos, la convención dicta NullPointerException se lanzará en lugar de IllegalArgumentException “.

Yo estaba a favor de arrojar IllegalArgumentException para los parámetros nulos, hasta hoy, cuando noté el método java.util.Objects.requireNonNull en Java 7. Con ese método, en vez de hacerlo:

 if (param == null) { throw new IllegalArgumentException("param cannot be null."); } 

tu puedes hacer:

 Objects.requireNonNull(param); 

y lanzará una NullPointerException si el parámetro que lo pasa es null .

Dado que ese método está justo en el medio de java.util , tomo su existencia como una indicación bastante fuerte de que lanzar NullPointerException 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 de hardware son falsos porque, por supuesto, puede proporcionar un mensaje a NullPointerException diciendo que era nulo y por qué no debería ser nulo. Al igual que con IllegalArgumentException .

Una ventaja adicional de NullPointerException es que, en el código crítico de alto rendimiento, puede prescindir de una comprobación explícita de nulo (y una NullPointerException con un mensaje de error amistoso), y simplemente confiar en la NullPointerException que obtendrá automáticamente cuando llame a un método en el parámetro nulo. Siempre que invoque 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 es mejor comprobarlo explícitamente y lanzar con un mensaje útil para indicar qué parámetro fue nulo, pero es bueno tener la opción de cambiarlo si el rendimiento lo dicta sin romper el contrato publicado del método / constructor.

Tiendo a seguir el diseño de las bibliotecas de 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 pro-activamente lanzan NullPointerException .

Por ejemplo, el Javadoc para Map.containsKey establece:

@throws NullPointerException si la clave es nula y este mapa no permite claves nulas (opcional).

Es perfectamente válido lanzar tu propio NPE. La convención es incluir el nombre del parámetro que fue nulo en el mensaje de la excepción.

El patrón va:

 public void someMethod(Object mustNotBeNull) { if (mustNotBeNull == null) { throw new NullPointerException("mustNotBeNull must not be null"); } } 

Hagas lo que hagas, no permitas que se establezca un valor incorrecto y lanza una excepción más adelante cuando otro código intente usarlo. Eso hace que la depuración sea una pesadilla. Siempre debe seguir el principio “fail-fast”.

Votó el argumento de Jason Cohen porque estaba bien presentado. Déjame desmembrarlo paso a paso. 😉

  • El ND JavaDoc dice explícitamente, “otros usos ilegales del objeto nulo” . Si solo se limita a situaciones en las que el tiempo de ejecución encuentra un valor nulo cuando no debería, todos esos casos podrían definirse de forma mucho más sucinta.

  • No puede evitarlo si asume lo incorrecto, pero suponiendo que la encapsulación se aplica correctamente, realmente no debería importarle ni darse cuenta si un nulo fue desreferenciado inapropiadamente, si un método detectó un nulo inapropiado y si se activó una excepción.

  • Elegiría NPE sobre IAE por múltiples razones

    • Es más específico sobre la naturaleza de la operación ilegal
    • La lógica que erróneamente permite nulos tiende a ser muy diferente de la lógica que erróneamente permite valores ilegales. Por ejemplo, si estoy validando datos ingresados ​​por un usuario, si obtengo un valor que no es aceptable, el origen de ese error es con el usuario final de la aplicación. Si obtengo un nulo, eso es un error del progtwigdor.
    • Los valores no válidos pueden causar desbordamientos de stack, errores de memoria, análisis de excepciones, etc. De hecho, la mayoría de los errores generalmente se presentan, en algún momento, como un valor no válido en alguna llamada a método. Por esta razón, veo que IAE es la MÁS GENERAL de todas las excepciones en RuntimeException.
  • En realidad, otros argumentos inválidos pueden dar como resultado todo tipo de otras excepciones. UnknownHostException , FileNotFoundException , una variedad de excepciones de error de syntax, IndexOutOfBoundsException , fallas de autenticación, etc., etc.

En general, creo que NPE es muy difamado porque tradicionalmente se ha asociado con código que no sigue el principio de fail fast . Eso, más el hecho de que el JDK no llenó 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.

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.

Si se trata de un método setter y se le pasa null , creo que tendría más sentido lanzar una IllegalArgumentException . Una NullPointerException parece tener más sentido en el caso en que intenta utilizar el null .

Entonces, si lo está usando y es null , NullPointer . Si se está IllegalArgument y es null , IllegalArgument .

Apache Commons Lang tiene una excepción 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 considero que arrojar algo como una NullArgumentException o IllegalArgumentException describe con mayor precisión las circunstancias excepcionales, mis colegas y yo hemos optado por ceder a los consejos de Bloch sobre el tema.

No podría estar más de acuerdo con lo que se dice. Fallar temprano, fallar rápido. Muy buen mantra de excepción.

La pregunta sobre qué Excepción arrojar 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

La práctica aceptada es si usar IllegalArgumentException (mensaje de cadena) para declarar que un parámetro no es válido y proporcionar tantos detalles como sea posible … Entonces, para decir que se encontró que un parámetro era nulo mientras que la excepción no era nula, se podía hacer algo Me gusta esto:

 if( variable == null ) throw new IllegalArgumentException("The object 'variable' cannot be null"); 

Prácticamente no tiene ninguna razón para usar implícitamente la “NullPointerException”. NullPointerException es una excepción lanzada por Java Virtual Machine cuando intenta ejecutar código en referencia nula (Like toString () ).

En realidad, la cuestión de arrojar IllegalArgumentException o NullPointerException es, en mi humilde opinión, solo una “guerra santa” para una minoría con una incomparable comprensión del manejo de excepciones en Java. En general, las reglas son simples, y de la siguiente manera:

  • las infracciones de restricción de argumentos se deben indicar lo más rápido posible (-> falla rápida), para evitar estados ilegales que son mucho más difíciles de depurar
  • en caso de un puntero nulo no válido por el motivo que sea, tira NullPointerException
  • en caso de un array / índice de colección ilegal, tira ArrayIndexOutOfBounds
  • en caso de una matriz negativa / tamaño de colección, tira NegativeArraySizeException
  • en caso de un argumento ilegal que no está cubierto por lo anterior, y para el cual no tiene otro tipo de excepción más específico, arroje IllegalArgumentException como una papelera
  • por otro lado, en el caso de una violación de constricción DENTRO DE UN CAMPO que no podría evitarse mediante una falla rápida por algún motivo válido, capture y vuelva a lanzar como IllegalStateException o una excepción comprobada más específica. ¡Nunca deje pasar la NullPointerException original, ArrayIndexOutOfBounds, etc. en este caso!

Hay al menos tres muy buenas razones contra el caso de mapear todo tipo de violaciones de restricción de argumento a IllegalArgumentException, con el tercero probablemente siendo tan severo como para marcar el estilo de mala práctica:

(1) Un progtwigdor no puede suponer con seguridad que todos los casos de violaciones de restricción de argumento dan como resultado IllegalArgumentException, porque la gran mayoría de las clases estándar usan esta excepción más bien como una papelera si no hay un tipo más específico de excepción disponible. Tratar de mapear todos los casos de violaciones de restricción de argumento a IllegalArgumentException en su API solo lleva a la frustración del progtwigdor al utilizar sus clases, ya que las bibliotecas estándar en su mayoría siguen reglas diferentes que violan las suyas, ¡y la mayoría de sus usuarios de API las usarán tambié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 admiten solo herencia simple. Por lo tanto, no hay forma de crear una excepción que realmente diga tanto una NullPointerException como una IllegalArgumentException, ya que las subclases solo pueden heredar de una u otra. Por lo tanto, lanzar una IllegalArgumentException en caso de un argumento nulo hace que sea más difícil para los usuarios de API distinguir entre los problemas cada vez que un progtwig intenta corregir el problema mediante progtwigción, por ejemplo, mediante la introducción de valores predeterminados en una repetición de llamada.

(3) El mapeo en realidad crea el peligro de enmascaramiento de errores: para mapear violaciones de restricción de argumentos en IllegalArgumentException, necesitará codificar un try-catch externo dentro de cada método que tenga argumentos restringidos. Sin embargo, simplemente atrapar RuntimeException en este bloque catch está fuera de la cuestión, porque corre el riesgo de correlacionar RuntimeExceptions documentados lanzados por los métodos libery usados ​​dentro de usted en IllegalArgumentException, incluso si no son causados ​​por violaciones de restricción de argumento. Por lo tanto, debe ser muy específico, pero incluso ese esfuerzo no lo protege del caso en que asigna accidentalmente una excepción de tiempo de ejecución no documentado de otra API (es decir, un error) en una excepción IllegalArgumentException de su API. Incluso el mapeo más cuidadoso corre el riesgo de enmascarar errores de progtwigción de otros bibliotecarios como violaciones de restricción de argumentos de los usuarios de su método, ¡lo cual es simplemente un comportamiento hilarante!

Con la práctica estándar, por otro lado, las reglas son simples, y las causas de excepción permanecen sin máscara y específicas. Para la persona que realiza el método, las reglas también son fáciles: – si encuentra una excepción de tiempo de ejecución documentada de algún tipo porque ha pasado un valor ilegal, repita la llamada con un valor predeterminado (para estas excepciones específicas son necesarias) o corrija su código – si, por otro lado, detecta una excepción de tiempo de ejecución que no está documentada para un determinado conjunto de argumentos, presente un informe de error a los creadores del método para asegurarse de que se corrige su código o su documentación.

Lanzar una excepción que sea exclusiva de null argumentos null (ya sea NullPointerException o un tipo personalizado) hace que null pruebas null automatizadas sean más confiables. Esta prueba automatizada se puede realizar con reflexión y un conjunto de valores predeterminados, como en NullPointerTester Guava . Por ejemplo, NullPointerTester intentará llamar al siguiente método …

 Foo(String string, List list) { checkArgument(string.length() > 0); // missing null check for list! this.string = string; this.list = list; } 

… con dos listas de argumentos: "", null y null, ImmutableList.of() . Probaría que cada una de estas llamadas arroja la NullPointerException esperada. Para esta implementación, pasar una lista null no produce NullPointerException . Sin embargo, produce una IllegalArgumentException porque sucede que NullPointerTester utiliza una cadena predeterminada de "" . Si NullPointerTester solo espera NullPointerException para valores null , detecta el error. Si espera IllegalArgumentException , lo pierde.

En general, un desarrollador nunca debe arrojar una NullPointerException. El tiempo de ejecución arroja esta excepción cuando el código intenta desreferenciar una variable cuyo valor es nulo. Por lo tanto, si su método desea rechazar explícitamente null, en lugar de simplemente tener un valor nulo, plantee una NullPointerException, debe lanzar una IllegalArgumentException.

Quería destacar los argumentos nulos de otros argumentos ilegales, por lo que obtuve una excepción de IAE llamada NullArgumentException. Sin siquiera necesitar leer el mensaje de excepción, sé que se pasó un argumento nulo a un método y al leer el mensaje, descubro cuál argumento era nulo. Sigo atrapando la excepción NullArgumentException con un controlador IAE, pero en mis registros es donde puedo ver la diferencia rápidamente.

la dicotomía … ¿No se superponen? Solo partes no superpuestas de un todo pueden hacer una dicotomía. Como yo lo veo:

 throw new IllegalArgumentException(new NullPointerException(NULL_ARGUMENT_IN_METHOD_BAD_BOY_BAD)); 

Algunas colecciones suponen que null se rechaza usando NullPointerException lugar de IllegalArgumentException . Por ejemplo, si compara un conjunto que contiene null con un conjunto que rechaza el null , el primer conjunto llamará a containsAll sobre el otro y detectará NullPointerException , pero no IllegalArgumentException . (Estoy viendo la implementación de AbstractSet.equals ).

Se podría argumentar razonablemente que usar excepciones sin marcar de esta manera es un antipatrón, que comparar colecciones que contienen null con colecciones que no pueden contener null es un error probable que realmente debería producir una excepción, o que poner null en una colección es en absoluto una mala idea. Sin embargo, a menos que esté dispuesto a decir que los equals deben arrojar una excepción en ese caso, está atrapado recordando que NullPointerException es necesaria en ciertas circunstancias, pero no en otras. (“IAE antes de NPE excepto después de ‘c’ …”)

NullPointerException lanzada al intentar acceder a un objeto con una variable de referencia cuyo valor actual es nulo

IllegalArgumentException lanzada cuando un método recibe un argumento formateado de forma diferente de lo que el método espera

Según su escenario, IllegalArgumentException es la mejor elección, porque null no es un valor válido para su propiedad.

Como pregunta subjetiva, esto debería cerrarse, 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. Esto es todo de memoria, así que no puedo recordar la redacción exacta. Vale la pena señalar que no usaron excepciones marcadas, pero eso está más allá del scope de la pregunta. Las excepciones no controladas que utilizaron se clasificaron en 3 categorías principales.

NullPointerException: no tirar intencionalmente. Los NPE solo deben ser lanzados por la máquina virtual cuando se desreferencia una referencia nula. Se debe hacer todo el esfuerzo posible para garantizar que nunca se lanzan. @Nullable y @NotNull se deben usar junto con las herramientas de análisis de código para encontrar estos errores.

IllegalArgumentException: Lanzada 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 que se transmiten. La situación del OP caería en esta categoría.

IllegalStateException: Lanzado cuando se llama a una función y sus argumentos son inesperados en el momento en que se pasan o son incompatibles con el estado del objeto al que pertenece el método.

Por ejemplo, había dos versiones internas de IndexOutOfBoundsException utilizadas en cosas que tenían una longitud. Una una subclase de IllegalStateException, utilizada si el índice era más grande que la longitud. La otra es una subclase de IllegalArgumentException, utilizada si el índice fue negativo. Esto se debe a que podrí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 muy bien, y a alguien le llevó explicar por qué existe esa distinción: “Dependiendo del tipo de error, es bastante sencillo para usted descubrir qué hacer. Incluso si no puede imaginarse qué salió mal, puedes 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 aserción es solo uno de los otros dos tipos. Si es posible, continúe la depuración como si la afirmación estuviera allí en primer lugar.

IllegalArgumentException: tiene algo mal en su sitio de llamadas. Si los valores que se pasan son de otra función, descubra por qué está recibiendo un valor incorrecto. Si está pasando en uno de sus argumentos propague el error comprueba la stack de llamadas hasta que encuentre la función que no está devolviendo lo que esperaba.

IllegalStateException: no ha llamado a sus funciones en el orden correcto. Si está utilizando uno de sus argumentos, revíselos y genere una IllegalArgumentException que describa el problema. A continuación, puede propagar las mejillas contra la stack hasta que encuentre el problema.

De todos modos, su punto era que solo puedes copiar las Aferencias IllegalArgument en la stack. There is no way for you to propagate the IllegalStateExceptions or NullPointerExceptions up the stack because they had something to do with your function.

Ideally runtime exceptions should not be thrown. A checked exception(business exception) should be created for your scenario. Because if either of these exception is thrown and logged, it misguides the developer while going through the logs. Instead business exceptions do not create that panic and usually ignored while troubleshooting logs.

The definitions from the links to the two exceptions above are IllegalArgumentException: Thrown to indicate that a method has been passed an illegal or inappropriate argument. NullPointerException: Thrown when an application attempts to use null in a case where an object is required.

The big difference here is the IllegalArgumentException is supposed to be used when checking that an argument to a method is valid. NullPointerException is supposed to be used whenever an object being “used” when it is null.

I hope that helps put the two in perspective.

If it’s a “setter”, or somewhere I’m getting a member to use later, I tend to use IllegalArgumentException.

If it’s something I’m going to use (dereference) right now in the method, I throw a NullPointerException proactively. I like this better than letting the runtime do it, because I can provide a helpful message (seems like the runtime could do this too, but that’s a rant for another day).

If I’m overriding a method, I use whatever the overridden method uses.

You should throw an IllegalArgumentException, as it will make it obvious to the programmer that he has done something invalid. Developers are so used to seeing NPE thrown by the VM, that any programmer would not immediately realize his error, and would start looking around randomly, or worse, blame your code for being ‘buggy’.

In this case, IllegalArgumentException conveys clear information to the user using your API that the ” should not be null”. As other forum users pointed out you could use NPE if you want to as long as you convey the right information to the user using your API.

GaryF and tweakt dropped “Effective Java” (which I swear by) references which recommends using NPE. And looking at how other good APIs are constructed is the best way to see how to construct your API.

Another good example is to look at the Spring APIs. For example, org.springframework.beans.BeanUtils.instantiateClass(Constructor ctor, Object[] args) has a Assert.notNull(ctor, “Constructor must not be null”) line. org.springframework.util.Assert.notNull(Object object, String message) method checks to see if the argument (object) passed in is null and if it is it throws a new IllegalArgumentException(message) which is then caught in the org.springframework.beans.BeanUtils.instantiateClass(…) method.

If you choose to throw a NPE and you are using the argument in your method, it might be redundant and expensive to explicitly check for a null. I think the VM already does that for you.