IllegalArgumentException o NullPointerException per un parametro null?

Ho un metodo di setter semplice per una proprietà e null non è appropriato per questa particolare proprietà. Sono sempre stato lacerato in questa situazione: dovrei lanciare un IllegalArgumentException o NullPointerException ? Dai javadocs, entrambi sembrano appropriati. C’è una specie di standard compreso? O è solo una di quelle cose che dovresti fare come preferisci ed entrambe sono veramente corrette?

Sembra che sia richiesto un IllegalArgumentException se non si desidera che null sia un valore consentito e la NullPointerException verrebbe generata se si stesse tentando di utilizzare una variabile che NullPointerException null .

Si dovrebbe usare IllegalArgumentException (IAE), non NullPointerException (NPE) per i seguenti motivi:

Innanzitutto, NPE JavaDoc elenca esplicitamente i casi in cui NPE è appropriato. Si noti che tutti vengono lanciati dal runtime quando null viene utilizzato in modo inappropriato. Al contrario, l’ IAE JavaDoc non potrebbe essere più chiaro: “Gettato per indicare che un metodo ha superato un argomento illegale o inappropriato.” Sì, sei tu!

In secondo luogo, quando si vede un NPE in una traccia di stack, cosa si assume? Probabilmente qualcuno ha cancellato un null . Quando vedi IAE, supponi che il chiamante del metodo in cima alla pila passi in un valore non valido. Ancora una volta, quest’ultima ipotesi è vera, la prima è fuorviante.

In terzo luogo, dal momento che IAE è chiaramente progettato per la convalida dei parametri, devi considerarlo come la scelta predefinita di eccezione, quindi perché dovresti scegliere NPE? Sicuramente non per un comportamento diverso – ti aspetti davvero che chiamare il codice per catturare gli NPE separatamente dall’IAE e fare qualcosa di diverso come risultato? Stai cercando di comunicare un messaggio di errore più specifico? Ma puoi farlo comunque nel testo del messaggio di eccezione, come dovresti per tutti gli altri parametri errati.

In quarto luogo, tutti gli altri dati dei parametri errati saranno IAE, quindi perché non essere coerenti? Perché un null illegale è così speciale da meritare un’eccezione separata da tutti gli altri tipi di argomenti illegali?

Infine, accetto l’argomento fornito da altre risposte che parti dell’API Java utilizzano NPE in questo modo. Tuttavia, l’API Java non è coerente con tutto, dai tipi di eccezioni alle convenzioni di denominazione, quindi penso che copiare in modo cieco (la parte preferita di) l’API Java non sia un argomento sufficientemente valido per trionfare su queste altre considerazioni.

Lo standard è di lanciare NullPointerException. L’infallibile “Effective Java” generalmente discute brevemente nella Voce 42 (prima edizione), nella Voce 60 (seconda edizione) o nella Voce 72 (terza edizione) “Favorire l’uso delle eccezioni standard”:

“Probabilmente, tutte le invocazioni di metodi errate si riducono a un argomento illegale o a uno stato illegale, ma altre eccezioni sono utilizzate normalmente per determinati tipi di argomenti e stati illegali. Se un chiamante passa null in alcuni parametri per i quali i valori nulli sono proibiti, la convenzione impone che NullPointerException può essere lanciato piuttosto che IllegalArgumentException. ”

Ero a favore del lancio di IllegalArgumentException per i parametri null, fino a oggi, quando ho notato il metodo java.util.Objects.requireNonNull in Java 7. Con quel metodo, invece di fare:

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

tu puoi fare:

 Objects.requireNonNull(param); 

e genererà una NullPointerException se il parametro che si passa è null .

Dato che quel metodo è giusto nel bel mezzo di java.util credo che la sua esistenza sia un’indicazione abbastanza forte che lanciare NullPointerException è “il modo di fare Java in Java”.

Penso di essere deciso in ogni caso.

Nota che gli argomenti riguardanti il ​​debugging sono falsi perché puoi ovviamente fornire un messaggio a NullPointerException dice cosa è nullo e perché non dovrebbe essere nullo. Proprio come con IllegalArgumentException .

Un vantaggio aggiunto di NullPointerException è che, nel codice altamente performante, è ansible fare a meno di un controllo esplicito per null (e una NullPointerException con un messaggio di errore NullPointerException ), e basta affidarsi alla NullPointerException si otterrà automaticamente quando si chiama un metodo sul parametro null. A condizione che tu chiami un metodo velocemente (ad es. Fallisci velocemente), allora hai essenzialmente lo stesso effetto, ma non altrettanto facilmente user-friendly per lo sviluppatore. La maggior parte delle volte è probabilmente meglio controllare esplicitamente e lanciare con un messaggio utile per indicare quale parametro era nullo, ma è bello avere la possibilità di cambiarlo se la prestazione impone senza rompere il contratto pubblicato del metodo / costruttore.

Io tendo a seguire il design delle librerie JDK, in particolare Collezioni e Concurrency (Joshua Bloch, Doug Lea, quei ragazzi sanno come progettare solide API). In ogni caso, molte API nel JDK NullPointerException triggersmente NullPointerException .

Ad esempio, Javadoc per Map.containsKey afferma:

@throws NullPointerException se la chiave è null e questa mappa non consente chiavi null (facoltative).

È perfettamente valido lanciare il tuo NPE. La convenzione deve includere il nome del parametro che era nullo nel messaggio dell’eccezione.

Il modello va:

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

Qualunque cosa tu faccia, non permettere che un valore cattivo venga impostato e lanciare un’eccezione più tardi quando un altro codice tenta di usarlo. Ciò rende il debug un incubo. Dovresti sempre seguire il principio “fail-fast”.

Ha votato l’argomentazione di Jason Cohen perché era ben presentata. Fammi smembrarlo passo dopo passo. 😉

  • NPE JavaDoc dice esplicitamente “altri usi illegali dell’object nullo” . Se era solo limitato a situazioni in cui il runtime incontra un valore null quando non dovrebbe, tutti questi casi potrebbero essere definiti in modo molto più succinto.

  • Non può essere d’aiuto se si assume la cosa sbagliata, ma supponendo che l’incapsulamento sia applicato correttamente, non si dovrebbe prestare attenzione o notare se un nullo è stato deferenziato in modo inappropriato rispetto a se un metodo ha rilevato un valore nullo inappropriato e ha distriggersto un’eccezione.

  • Sceglierei NPE su IAE per molteplici ragioni

    • È più specifico sulla natura dell’operazione illegale
    • La logica che consente erroneamente i valori nulli tende ad essere molto diversa dalla logica che erroneamente consente valori illegali. Ad esempio, se convalido i dati immessi da un utente, se ottengo un valore inaccettabile, l’origine di tale errore è con l’utente finale dell’applicazione. Se ottengo un null, questo è un errore del programmatore.
    • Valori non validi possono causare cose come lo stack overflow, gli errori di memoria esaurita, l’analisi delle eccezioni, ecc. In effetti, la maggior parte degli errori in genere è presente, ad un certo punto, come valore non valido in alcune chiamate di metodo. Per questo motivo vedo IAE come il MOST GENERALE di tutte le eccezioni in RuntimeException.
  • In realtà, altri argomenti non validi possono generare tutti i tipi di altre eccezioni. UnknownHostException , FileNotFoundException , una varietà di eccezioni degli errori di syntax, IndexOutOfBoundsException , errori di autenticazione, ecc., Ecc.

In generale, ritengo che NPE sia molto diffamato perché tradizionalmente è stato associato al codice che non segue il principio del fail fast . Questo, oltre a non riuscire a popolare NPE con una stringa di messaggi, ha creato un forte sentimento negativo che non è ben fondato. In effetti, la differenza tra NPE e IAE da una prospettiva di runtime è strettamente il nome. Da quella prospettiva, più preciso sei con il nome, più chiarezza darai al chiamante.

È una domanda in stile “Guerra santa”. In altre parole, entrambe le alternative sono buone, ma le persone avranno le loro preferenze che difenderanno fino alla morte.

Se si tratta di un metodo setter e viene passato a null , penso che avrebbe più senso lanciare un IllegalArgumentException . Una NullPointerException sembra avere più senso nel caso in cui si stia tentando di utilizzare effettivamente il null .

Quindi, se lo stai usando ed è null , NullPointer . Se viene passato e è null , IllegalArgument .

Apache Commons Lang ha una NullArgumentException che fa un certo numero di cose discusse qui: estende IllegalArgumentException e il suo unico costruttore prende il nome dell’argomento che avrebbe dovuto essere non-null.

Mentre sento che lanciare qualcosa come NullArgumentException o IllegalArgumentException descrive in modo più accurato le circostanze eccezionali, i miei colleghi e io abbiamo scelto di rimandare il consiglio di Bloch sull’argomento.

Non potrei essere più d’accordo con quello che viene detto. Fallire presto, fallire velocemente Abbastanza buono mantra eccezione.

La domanda su quale eccezione lanciare è principalmente una questione di gusto personale. Nella mia mente IllegalArgumentException sembra più specifico dell’utilizzo di un NPE poiché mi dice che il problema era con un argomento che ho passato al metodo e non con un valore che potrebbe essere stato generato durante l’esecuzione del metodo.

I miei 2 centesimi

La pratica accettata se si utilizza l’ IllegalArgumentException (messaggio String) per dichiarare un parametro non valido e fornire il maggior numero di dettagli ansible … Quindi per dire che un parametro è stato trovato come null mentre l’eccezione non-null, si farebbe qualcosa come questo:

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

Non hai praticamente alcun motivo per utilizzare implicitamente la “NullPointerException”. NullPointerException è un’eccezione generata dalla Java Virtual Machine quando si tenta di eseguire codice su riferimento null (Like toString () ).

In realtà, la questione del lancio di IllegalArgumentException o NullPointerException è nella mia modesta opinione solo una “guerra santa” per una minoranza con una comprensione incompleta della gestione delle eccezioni in Java. In generale, le regole sono semplici e come segue:

  • le violazioni dei vincoli degli argomenti devono essere indicate il più rapidamente ansible (-> fail veloce), al fine di evitare stati illegali che sono molto più difficili da eseguire il debug
  • in caso di un puntatore nullo non valido per qualsiasi motivo, lanciare NullPointerException
  • in caso di un array / indice di raccolta illegale, lanciare ArrayIndexOutOfBounds
  • in caso di dimensioni dell’array / raccolta negativo, lanciare NegativeArraySizeException
  • nel caso di un argomento illegale che non è coperto da quanto sopra, e per il quale non si dispone di un altro tipo di eccezione più specifico, lanciare IllegalArgumentException come cestino della carta straccia
  • d’altra parte, in caso di violazione di un vincolo ENTRO UN CAMPO che non può essere evitato da un errore rapido per qualche motivo valido, prendere e rilanciare come IllegalStateException o un’eccezione controllata più specifica. In questo caso, non lasciare mai passare l’originale NullPointerException, ArrayIndexOutOfBounds, ecc.

Ci sono almeno tre ottime ragioni contro il caso di mappare tutti i tipi di violazioni del vincolo degli argomenti a IllegalArgumentException, con il terzo probabilmente così severo da segnare lo stile cattivo della pratica:

(1) Un programmatore non può presumere che tutti i casi di violazioni dei vincoli degli argomenti risultino in IllegalArgumentException, perché la grande maggioranza delle classi standard usa questa eccezione piuttosto come un cestino dei rifiuti se non esiste un tipo specifico di eccezione disponibile. Provare a mappare tutti i casi di violazioni dei vincoli degli argomenti a IllegalArgumentException nella tua API porta solo alla frustrazione del programmatore usando le tue classi, in quanto le librerie standard seguono per lo più regole diverse che violano le tue e la maggior parte degli utenti API le userà!

(2) La mapping delle eccezioni risulta in realtà in un diverso tipo di anomalia, causato dall’ereditarietà singola: Tutte le eccezioni Java sono classi e pertanto supportano solo l’ereditarietà singola. Pertanto, non esiste un modo per creare un’eccezione che sia in grado di dire sia una NullPointerException sia un’IllegalArgumentException, in quanto le sottoclassi possono ereditare solo dall’una o dall’altra. Lanciare un IllegalArgumentException in caso di argomento nullo rende quindi più difficile agli utenti API distinguere tra i problemi ogni volta che un programma tenta di correggere il problema a livello di programmazione, ad esempio inserendo i valori predefiniti in una ripetizione di chiamata!

(3) La mapping crea effettivamente il pericolo di mascheramento dei bug: per mappare le violazioni dei vincoli degli argomenti in IllegalArgumentException, è necessario codificare un try-catch esterno all’interno di ogni metodo con argomenti vincolati. Tuttavia, la semplice cattura di RuntimeException in questo blocco di cattura è fuori questione, poiché rischia di mappare le RuntimeException documentate generate dai metodi di liberia utilizzati all’interno di IllegalArgumentException, anche se non sono causati da violazioni dei vincoli degli argomenti. Quindi devi essere molto specifico, ma anche questo sforzo non ti protegge dal caso in cui accidentalmente mappassi un’eccezione di runtime non documentata di un’altra API (cioè un bug) in un IllegalArgumentException della tua API. Anche la mapping più attenta rischia quindi di mascherare gli errori di programmazione di altri produttori di librerie come violazioni dei vincoli di argomento degli utenti del metodo, il che è semplicemente un comportamento collinoso!

Con la pratica standard d’altra parte, le regole rimangono semplici e le cause di eccezione rimangono non mascherate e specifiche. Per il chiamante del metodo, anche le regole sono facili: – se incontri un’eccezione di runtime documentata di qualsiasi tipo perché hai passato un valore non valido, ripeti la chiamata con un valore predefinito (per le specifiche eccezioni sono necessarie) o correggi il tuo codice – se invece incontri un’eccezione di runtime che non è documentata per un determinato set di argomenti, invia una segnalazione di bug ai responsabili del metodo per assicurarti che il loro codice o la loro documentazione siano corretti.

Lanciare un’eccezione esclusiva degli argomenti null (che sia NullPointerException o un tipo personalizzato) rende il test null automatizzato più affidabile. Questo test automatizzato può essere eseguito con la riflessione e un insieme di valori predefiniti, come nel NullPointerTester Guava . Ad esempio, NullPointerTester di chiamare il seguente metodo …

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

… con due elenchi di argomenti: "", null e null, ImmutableList.of() . NullPointerException che ognuna di queste chiamate genera l’attesa NullPointerException . Per questa implementazione, il passaggio di un elenco null non produce NullPointerException . Tuttavia, ciò avviene producendo un IllegalArgumentException perché NullPointerTester di utilizzare una stringa predefinita di "" . Se NullPointerTester prevede solo NullPointerException per valori null , NullPointerException il bug. Se si aspetta IllegalArgumentException , lo manca.

In generale, uno sviluppatore non dovrebbe mai lanciare una NullPointerException. Questa eccezione viene generata dal runtime quando il codice tenta di dereferenziare una variabile il cui valore è nullo. Pertanto, se il tuo metodo vuole disabilitare esplicitamente null, al contrario di avere un valore nullo sollevare una NullPointerException, devi lanciare un IllegalArgumentException.

Volevo individuare argomenti Null da altri argomenti illegali, quindi ho ricavato un’eccezione da IAE denominata NullArgumentException. Senza nemmeno bisogno di leggere il messaggio di eccezione, so che un argomento nullo è stato passato in un metodo e leggendo il messaggio, ho scoperto quale argomento era nullo. Ricevo ancora NullArgumentException con un gestore IAE, ma nei miei registri è ansible vedere rapidamente la differenza.

la dicotomia … Sono non sovrapposti? Solo le parti non sovrapposte di un tutto possono fare una dicotomia. Per come la vedo:

 throw new IllegalArgumentException(new NullPointerException(NULL_ARGUMENT_IN_METHOD_BAD_BOY_BAD)); 

Alcune raccolte presuppongono che il rifiuto venga rifiutato utilizzando NullPointerException anziché IllegalArgumentException . Ad esempio, se si confronta un set contenente null con un set che rifiuta null , il primo set chiamerà containsAll sull’altro e NullPointerException sua NullPointerException – ma non IllegalArgumentException . (Sto osservando l’implementazione di AbstractSet.equals ).

Si potrebbe ragionevolmente sostenere che l’utilizzo di eccezioni non controllate in questo modo è un antipattern, che il confronto di collezioni che contengono null in raccolte che non possono contenere null è un probabile bug che dovrebbe produrre un’eccezione, o che mettere null in una raccolta è una ctriggers idea. Tuttavia, a meno che tu non sia disposto a dire che gli equals dovrebbero gettare un’eccezione in questo caso, sei bloccato ricordando che NullPointerException è richiesto in determinate circostanze ma non in altre. (“IAE prima di NPE tranne dopo ‘c’ …”)

NullPointerException generata quando si tenta di accedere a un object con una variabile di riferimento il cui valore corrente è null

IllegalArgumentException generato quando un metodo riceve un argomento formattato in modo diverso rispetto a quanto previsto dal metodo

Secondo il tuo scenario, IllegalArgumentException è la scelta migliore, perché null non è un valore valido per la tua proprietà.

Come domanda soggettiva, questa dovrebbe essere chiusa, ma poiché è ancora aperta:

Questo fa parte della politica interna utilizzata nella mia precedente sede di lavoro e ha funzionato molto bene. Questo è tutto dalla memoria, quindi non posso ricordare la formulazione esatta. Vale la pena notare che non hanno usato eccezioni controllate, ma questo va oltre lo scopo della domanda. Le eccezioni incontrollate che usavano rientravano in 3 categorie principali.

NullPointerException: non lanciare intenzionalmente. I NPE devono essere lanciati solo dalla VM quando si denota un riferimento null. Bisogna fare tutto il ansible per garantire che non vengano mai lanciati. @Nullable e @NotNull dovrebbero essere usati insieme agli strumenti di analisi del codice per trovare questi errori.

IllegalArgumentException: generata quando un argomento di una funzione non è conforms alla documentazione pubblica, in modo tale che l’errore possa essere identificato e descritto in termini degli argomenti passati. La situazione dell’OP rientrerebbe in questa categoria.

IllegalStateException: generata quando viene chiamata una funzione e i relativi argomenti sono imprevisti nel momento in cui vengono passati o incompatibili con lo stato dell’object di cui il metodo è membro.

Ad esempio, c’erano due versioni interne di IndexOutOfBoundsException utilizzate in cose che avevano una lunghezza. Una sottoclass di IllegalStateException, usata se l’indice era più grande della lunghezza. L’altra una sottoclass di IllegalArgumentException, usata se l’indice era negativo. Questo perché potresti aggiungere più oggetti all’object e l’argomento sarebbe valido, mentre un numero negativo non è mai valido.

Come ho detto, questo sistema funziona davvero bene, e ci è voluto qualcuno per spiegare perché la distinzione ci sia: “A seconda del tipo di errore è abbastanza semplice per te capire cosa fare. Anche se non puoi davvero capire fuori da ciò che è andato storto, puoi capire dove rilevare l’errore e creare ulteriori informazioni di debug. ”

NullPointerException: gestisce il caso Null o inserisce un’asserzione in modo che l’NPE non venga lanciato. Se inserisci un’affermazione è solo uno degli altri due tipi. Se ansible, continua a eseguire il debug come se l’asserzione fosse presente in primo luogo.

IllegalArgumentException: hai qualcosa di sbagliato nel tuo sito di chiamata. Se i valori che vengono inoltrati provengono da un’altra funzione, scopri perché stai ricevendo un valore errato. Se stai trasmettendo uno dei tuoi argomenti, propagare l’errore controlla lo stack delle chiamate finché non trovi la funzione che non restituisce ciò che ti aspetti.

IllegalStateException: non hai chiamato le tue funzioni nell’ordine corretto. Se stai utilizzando uno dei tuoi argomenti, controllali e lancia un IllegalArgumentException che descrive il problema. È quindi ansible propagare le guance contro lo stack fino a trovare il problema.

Anyway, his point was that you can only copy the IllegalArgumentAssertions up the 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.