Domanda

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

È stato utile?

Soluzione

Sembra un IllegalArgumentException è richiesto se non vuoi null essere un valore consentito e il NullPointerException verrebbe lanciato se ci provassi utilizzo una variabile che risulta essere null.

Altri suggerimenti

Dovresti usarlo IllegalArgumentException (IAE), no NullPointerException (NPE) per i seguenti motivi:

Prima il NPE JavaDoc elenca esplicitamente i casi in cui la NPE è appropriata.Nota che vengono tutti lanciati dal tempo di esecuzione Quando null viene utilizzato in modo inappropriato.Al contrario, il IAE JavaDoc non potrebbe essere più chiaro:"Gettato per indicare che un metodo è stato approvato un argomento illegale o inappropriato." Sì, sei tu!

In secondo luogo, quando vedi un NPE in uno stack trace, cosa presumi?Probabilmente che qualcuno abbia dereferenziato a null.Quando vedi IAE, presumi che il chiamante del metodo in cima allo stack abbia passato un valore illegale.Ancora una volta, la seconda ipotesi è vera, la prima è fuorviante.

In terzo luogo, poiché IAE è chiaramente progettato per convalidare i parametri, devi assumerlo come scelta predefinita di eccezione, quindi perché dovresti scegliere invece NPE?Certamente non per un comportamento diverso: ti aspetti davvero che il codice chiamante catturi gli NPE separatamente da IAE e di conseguenza faccia qualcosa di diverso?Stai tentando di comunicare un messaggio di errore più specifico?Ma puoi farlo comunque nel testo del messaggio di eccezione, come dovresti fare per tutti gli altri parametri errati.

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

Infine, accetto l'argomentazione fornita da altre risposte secondo cui parti dell'API Java utilizzano NPE in questo modo.Tuttavia, l'API Java non è coerente con tutto, dai tipi di eccezione alle convenzioni di denominazione, quindi penso che semplicemente copiare ciecamente (la tua parte preferita) dell'API Java non sia un argomento sufficiente per prevalere su queste altre considerazioni.

Lo standard è lanciare il NullPointerException.Il generalmente infallibile "Effective Java" ne discute brevemente nell'Articolo 42 (prima edizione), Articolo 60 (seconda edizione) o Articolo 72 (terza edizione) "Favorire l'uso di eccezioni standard":

"Probabilmente, tutte le invocazioni di metodi errate si riducono a un argomento illegale o uno stato illegale, ma altre eccezioni sono usate standardmente per determinati tipi di argomenti e stati illegali.Se un chiamante passa NULL in alcuni parametri per il quale sono vietati i valori nulli, la convenzione impone che venga lanciato NullPointerException anziché illegalArgumentException. "

Ero assolutamente favorevole al lancio IllegalArgumentException per parametri nulli, fino ad oggi, quando ho notato il file java.util.Objects.requireNonNull metodo in Java 7.Con questo metodo, invece di fare:

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

tu puoi fare:

Objects.requireNonNull(param);

e lancerà a NullPointerException se il parametro che passi lo è null.

Dato che quel metodo è proprio nel mezzo java.util Ritengo che la sua esistenza sia un'indicazione piuttosto forte del lancio NullPointerException è "il modo Java di fare le cose".

Penso di essere deciso in ogni caso.

Tieni presente che gli argomenti sul debugging rigido sono fasulli perché ovviamente puoi fornire un messaggio NullPointerException dicendo cosa era nullo e perché non dovrebbe essere nullo.Proprio come con IllegalArgumentException.

Un ulteriore vantaggio di NullPointerException è che, nel codice critico ad alte prestazioni, potresti fare a meno di un controllo esplicito per null (e a NullPointerException con un messaggio di errore amichevole) e fare affidamento solo su NullPointerException otterrai automaticamente quando chiami un metodo sul parametro null.A condizione che tu chiami rapidamente un metodo (ad es.fallire velocemente), si ottiene essenzialmente lo stesso effetto, solo che non è così facile da usare per lo sviluppatore.La maggior parte delle volte è probabilmente meglio controllare esplicitamente e lanciare un messaggio utile per indicare quale parametro era nullo, ma è bello avere la possibilità di modificarlo se le prestazioni lo richiedono senza infrangere il contratto pubblicato del metodo/costruttore.

Tendo a seguire la progettazione delle librerie JDK, in particolare raccolte e concorrenza (Joshua Bloch, Doug Lea, quei ragazzi sanno come progettare API solide).Ad ogni modo, molte API nel JDK lanciano in modo proattivo NullPointerException.

Ad esempio, Javadoc per Map.containsKey stati:

@Throws NullPointerException Se la chiave è null e questa mappa non consente i tasti null (opzionale).

È perfettamente legittimo lanciare il proprio NPE.La convenzione prevede di includere il nome del parametro che era nullo nel messaggio dell'eccezione.

Lo schema è:

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

Qualunque cosa tu faccia, non consentire che venga impostato un valore errato e generare un'eccezione in seguito quando altro codice tenta di utilizzarlo.Ciò rende il debug un incubo.Dovresti sempre seguire il principio "fail-fast".

Ho votato l'argomentazione di Jason Cohen perché era ben presentata.Lasciami smembrarlo passo dopo passo.;-)

  • IL NPE JavaDoc dice esplicitamente, "altri usi illegali dell'oggetto null".Se fosse limitato solo alle situazioni in cui il runtime incontra un valore nullo quando non dovrebbe, tutti questi casi potrebbero essere definiti in modo molto più succinto.

  • Non puoi farci niente se presumi la cosa sbagliata, ma presupponendo che l'incapsulamento sia applicato correttamente, non dovresti davvero preoccuparti o notare se un null è stato dereferenziato in modo inappropriato rispetto ase un metodo ha rilevato un valore null inappropriato e ha generato un'eccezione.

  • Sceglierei NPE Sopra IAE per molteplici ragioni

    • È più specifico riguardo alla natura dell'operazione illegale
    • La logica che erroneamente consente valori nulli tende ad essere molto diversa dalla logica che erroneamente consente valori illegali.Ad esempio, se sto convalidando i dati immessi da un utente, se ottengo un valore inaccettabile, l'origine dell'errore riguarda l'utente finale dell'applicazione.Se ottengo un valore nullo, si tratta di un errore del programmatore.
    • Valori non validi possono causare cose come overflow dello stack, errori di memoria insufficiente, eccezioni di analisi, ecc.In effetti, la maggior parte degli errori generalmente si presenta, ad un certo punto, come valore non valido in qualche chiamata al metodo.Per questo motivo considero l'IAE effettivamente il PIÙ GENERALE di tutte le eccezioni in RuntimeException.
  • In realtà, altri argomenti non validi possono comportare tutti i tipi di altre eccezioni. UnknownHostException, FileNotFoundException, una serie di eccezioni di errori di sintassi, IndexOutOfBoundsException, errori di autenticazione, ecc., ecc.

In generale, ritengo che l'NPE sia molto diffamato perché tradizionalmente è stato associato a un codice che non riesce a seguire il codice principio del fallimento veloce.Ciò, oltre al fallimento del JDK nel popolare gli NPE con una stringa di messaggio, ha davvero creato un forte sentimento negativo che non è ben fondato.In effetti, la differenza tra NPE e IAE dal punto di vista del runtime è strettamente legata al nome.Da questo punto di vista, più sei preciso con il nome, più chiarezza dai 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 è un setter metodo e null gli viene passato, penso che avrebbe più senso lanciare un file IllegalArgumentException.UN NullPointerException sembra avere più senso nel caso in cui stai tentando di utilizzare effettivamente il file null.

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

Apache Commons Lang ha un file NullArgumentException questo fa una serie di cose discusse qui:estende IllegalArgumentException e il suo unico costruttore prende il nome dell'argomento che avrebbe dovuto essere diverso da null.

Anche se ritengo che lanciare qualcosa come NullArgumentException o IllegalArgumentException descriva in modo più accurato le circostanze eccezionali, io e i miei colleghi abbiamo scelto di rimetterci al consiglio di Bloch sull'argomento.

Non potrei essere più d'accordo con ciò che viene detto.Fallisci presto, fallisci velocemente.Mantra dell'eccezione abbastanza buono.

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

I miei 2 centesimi

La pratica accettata è quella di utilizzare il file IllegalArgumentException(Messaggio stringa) per dichiarare non valido un parametro e fornire quanti più dettagli possibili...Quindi, per dire che un parametro è risultato nullo mentre un'eccezione non è nulla, dovresti fare qualcosa del genere:

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

Non hai praticamente alcun motivo per utilizzare implicitamente la "NullPointerException".La NullPointerException è un'eccezione generata dalla Java Virtual Machine quando si tenta di eseguire codice su riferimento null (Like accordare()).

In realtà, la questione di lanciare IllegalArgumentException o NullPointerException è, a mio modesto avviso, 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ù velocemente possibile (-> fast fail), per evitare stati illegali di cui è molto più difficile eseguire il debug
  • in caso di puntatore null non valido per qualsiasi motivo, lancia NullPointerException
  • in caso di un indice di array/raccolta illegale, lancia ArrayIndexOutOfBounds
  • in caso di dimensione di array/raccolta negativa, lancia NegativeArraySizeException
  • in caso di un argomento illegale che non è coperto da quanto sopra e per il quale non disponi di un altro tipo di eccezione più specifico, lancia IllegalArgumentException come cestino dei rifiuti
  • d'altra parte, in caso di violazione di un vincolo ALL'INTERNO DI UN CAMPO che non può essere evitato da un fallimento rapido per qualche motivo valido, cattura e rilancia come IllegalStateException o un'eccezione controllata più specifica.Non lasciare mai passare l'originale NullPointerException, ArrayIndexOutOfBounds, ecc. in questo caso!

Ci sono almeno tre ottime ragioni contro il caso di mappare tutti i tipi di violazioni dei vincoli di argomento su IllegalArgumentException, con la terza probabilmente così grave da contrassegnare la pratica di cattivo stile:

(1) Un programmatore non può presumere con sicurezza che tutti i casi di violazione dei vincoli degli argomenti risultino in IllegalArgumentException, perché la grande maggioranza delle classi standard utilizza questa eccezione piuttosto come un cestino dei rifiuti se non è disponibile un tipo di eccezione più specifico.Cercare di mappare tutti i casi di violazioni dei vincoli degli argomenti su IllegalArgumentException nella tua API porta solo alla frustrazione del programmatore nell'usare le tue classi, poiché le librerie standard seguono principalmente regole diverse che violano le tue e anche la maggior parte degli utenti API le utilizzerà!

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

(3) La mappatura crea effettivamente il pericolo di mascheramento dei bug:Per mappare le violazioni dei vincoli degli argomenti in IllegalArgumentException, dovrai codificare un try-catch esterno all'interno di ogni metodo che abbia argomenti vincolati.Tuttavia, catturare semplicemente RuntimeException in questo blocco catch è fuori questione, perché ciò rischia di mappare le RuntimeException documentate lanciate dai metodi libery utilizzati nel tuo in IllegalArgumentException, anche se non sono causate da violazioni dei vincoli degli argomenti.Quindi devi essere molto specifico, ma anche questo sforzo non ti protegge dal caso in cui mappi accidentalmente un'eccezione di runtime non documentata di un'altra API (ad es.un bug) in una IllegalArgumentException della tua API.Anche la mappatura più attenta rischia quindi di mascherare gli errori di programmazione di altri produttori di librerie come violazioni dei vincoli degli argomenti degli utenti del tuo metodo, il che è semplicemente un comportamento assurdo!

Con la pratica standard, invece, le regole rimangono semplici e le cause delle eccezioni rimangono non mascherate e specifiche.Anche per il chiamante del metodo le regole sono semplici:- Se si verifica un'eccezione di runtime documentata di qualsiasi tipo perché hai superato un valore illegale, ripeti la chiamata con un valore predefinito (per queste eccezioni specifiche sono necessarie) o correggi il codice - se invece si avvia un'eccezione di runtime non è documentato che accada per un determinato set di argomenti, presentare una segnalazione di bug ai produttori del metodo per garantire che il loro codice o la loro documentazione siano fissati.

In generale, uno sviluppatore dovrebbe Mai lancia una NullPointerException.Questa eccezione viene generata dal runtime quando il codice tenta di dereferenziare una variabile il cui valore è null.Pertanto, se il tuo metodo vuole impedire esplicitamente null, invece di avere semplicemente un valore null che solleva una NullPointerException, dovresti lanciare una IllegalArgumentException.

Genera un'eccezione esclusiva di null argomenti (se NullPointerException o un tipo personalizzato) rende automatizzato null test più affidabili.Questo test automatizzato può essere eseguito con la riflessione e un insieme di valori predefiniti, come in Guaiava'S NullPointerTester.Per esempio, NullPointerTester proverei a 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().Verificherebbe che ciascuna di queste chiamate genera quanto previsto NullPointerException.Per questa implementazione, passando a null la lista lo fa non produrre NullPointerException.Tuttavia, capita di produrre un IllegalArgumentException Perché NullPointerTester sembra utilizzare una stringa predefinita di "".Se NullPointerTester si aspetta solo NullPointerException per null valori, rileva il bug.Se lo aspetta IllegalArgumentException, gli manca.

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

Questo fa parte della politica interna utilizzata nel mio precedente posto di lavoro e ha funzionato davvero bene.Questo viene tutto dalla memoria, quindi non riesco a ricordare la formulazione esatta.Vale la pena notare che non hanno utilizzato eccezioni controllate, ma questo va oltre lo scopo della domanda.Le eccezioni non controllate utilizzate rientravano in 3 categorie principali.

NullPointerException:Non lanciare intenzionalmente.Gli NPE devono essere lanciati solo dalla VM quando si dereferenzia un riferimento nullo.Bisogna fare tutto il possibile per garantire che questi non vengano mai lanciati.@Nullable e @NotNull dovrebbero essere utilizzati insieme agli strumenti di analisi del codice per trovare questi errori.

IllegalArgumentException:Emesso quando un argomento di una funzione non è conforme alla documentazione pubblica, in modo tale che l'errore possa essere identificato e descritto in termini di argomenti passati.La situazione del PO rientrerebbe in questa categoria.

IllegalStateException:Emesso quando viene chiamata una funzione e i suoi argomenti sono inattesi nel momento in cui vengono passati o incompatibili con lo stato dell'oggetto di cui è membro il metodo.

Ad esempio, c'erano due versioni interne di IndexOutOfBoundsException utilizzate in cose che avevano una lunghezza.Una è una sottoclasse di IllegalStateException, utilizzata se l'indice era maggiore della lunghezza.L'altra è una sottoclasse di IllegalArgumentException, utilizzata se l'indice era negativo.Questo perché potresti aggiungere più elementi all'oggetto e l'argomento sarebbe valido, mentre un numero negativo non è mai valido.

Come ho detto, questo sistema funziona davvero bene, e ci voleva qualcuno che spiegasse perché esiste questa distinzione:"A seconda del tipo di errore è abbastanza semplice capire cosa fare.Anche se non riesci a capire cosa è andato storto, puoi capire dove individuare l'errore e creare ulteriori informazioni di debug."

NullPointerException:Gestire il caso Null o inserire un'asserzione in modo che l'NPE non venga generato.Se inserisci un'affermazione è solo uno degli altri due tipi.Se possibile, continuare il debug come se l'asserzione fosse già presente.

IllegalArgumentException:hai qualcosa che non va sul tuo sito di chiamata.Se i valori trasmessi provengono da un'altra funzione, scopri perché ricevi un valore errato.Se stai passando uno dei tuoi argomenti, propaga l'errore controlla lo stack di 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'eccezione IllegalArgumentException che descrive il problema.Puoi quindi propagare le guance contro la pila finché non trovi il problema.

Ad ogni modo, il suo punto era che puoi copiare solo IllegalArgumentAssertions nello stack.Non è possibile propagare le IllegalStateException o le NullPointerException nello stack perché avevano qualcosa a che fare con la tua funzione.

Volevo distinguere gli argomenti Null da altri argomenti illegali, quindi ho derivato un'eccezione da IAE denominata NullArgumentException.Senza nemmeno aver bisogno di leggere il messaggio di eccezione, so che un argomento nullo è stato passato a un metodo e leggendo il messaggio scopro quale argomento era nullo.Rilevo ancora la NullArgumentException con un gestore IAE, ma è nei miei log che posso vedere rapidamente la differenza.

la dicotomia...Non sono sovrapponibili?Solo le parti non sovrapposte di un tutto possono creare una dicotomia.Per come la vedo:

throw new IllegalArgumentException(new NullPointerException(NULL_ARGUMENT_IN_METHOD_BAD_BOY_BAD));

Alcune collezioni lo presuppongono null viene rifiutato utilizzando NullPointerException piuttosto che IllegalArgumentException.Ad esempio, se confronti un set contenente null a un insieme che rifiuta null, il primo set chiamerà containsAll dall'altro e prendilo NullPointerException -- ma no IllegalArgumentException.(Sto guardando l'implementazione di AbstractSet.equals.)

Si potrebbe ragionevolmente sostenere che l'utilizzo di eccezioni non controllate in questo modo è un antipattern, che confronta le raccolte che contengono null alle raccolte che non possono contenere null è un probabile bug davvero Dovrebbe produrre un'eccezione, o quella messa null in una collezione è una cattiva idea.Tuttavia, a meno che tu non sia disposto a dirlo equals dovrebbe generare un'eccezione in questo caso, sei bloccato a ricordartelo NullPointerException è richiesto in alcune circostanze ma non in altre.("IAE prima di NPE tranne dopo 'c'...")

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

IllegalArgumentException generata quando un metodo riceve un argomento formattato diversamente da quanto previsto dal metodo

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

Idealmente le eccezioni di runtime non dovrebbero essere generate.È necessario creare un'eccezione controllata (eccezione aziendale) per il tuo scenario.Perché se una di queste eccezioni viene generata e registrata, lo sviluppatore viene fuorviato durante l'analisi dei registri.Invece le eccezioni aziendali non creano quel panico e vengono solitamente ignorate durante la risoluzione dei problemi dei registri.

Le definizioni dai collegamenti alle due eccezioni sopra sono illeglargumentException:Lanciato per indicare che a un metodo è stato passato un argomento illegale o inappropriato.NullPointerException:Emesso quando un'applicazione tenta di utilizzare null nel caso in cui è richiesto un oggetto.

La grande differenza qui è che IllegalArgumentException dovrebbe essere utilizzata quando si verifica che un argomento di un metodo sia valido.Si suppone che NullPointerException venga utilizzato ogni volta che un oggetto viene "utilizzato" quando è nullo.

Spero che questo aiuti a mettere i due in prospettiva.

Se è un "setter", o da qualche parte sto ottenendo un membro da utilizzare in seguito, tendo a utilizzare IllegalArgumentException.

Se è qualcosa che utilizzerò (dereferenziazione) proprio ora nel metodo, lancio una NullPointerException in modo proattivo.Questo mi piace di più che lasciare che sia il runtime a farlo, perché posso fornire un messaggio utile (sembra che anche il runtime possa fare lo stesso, ma questo è uno sfogo per un altro giorno).

Se sto sovrascrivendo un metodo, utilizzo qualunque cosa utilizzi il metodo sovrascritto.

Dovresti lanciare una IllegalArgumentException, poiché renderà ovvio al programmatore che ha fatto qualcosa di non valido.Gli sviluppatori sono così abituati a vedere NPE generato dalla VM, che qualsiasi programmatore non si renderebbe immediatamente conto del suo errore e inizierebbe a guardarsi intorno in modo casuale o, peggio, a incolpare il codice per essere "buggy".

In questo caso, IllegalArgumentException trasmette informazioni chiare all'utente utilizzando la tua API che " non dovrebbe essere nullo".Come hanno sottolineato altri utenti del forum, potresti utilizzare NPE se lo desideri purché trasmetti le informazioni giuste all'utente utilizzando la tua API.

GaryF e tweakt hanno eliminato i riferimenti "Effective Java" (su cui lo giuro) che consigliano di utilizzare NPE.E osservare come vengono costruite altre buone API è il modo migliore per vedere come costruire la tua API.

Un altro buon esempio è guardare le API Spring.Ad esempio, org.springframework.beans.BeanUtils.instantiateClass(Constructor ctor, Object[] args) ha una riga Assert.notNull(ctor, "Il costruttore non deve essere null").Il metodo org.springframework.util.Assert.notNull(Object object, String message) controlla se l'argomento (oggetto) passato è null e se lo è lancia una nuova IllegalArgumentException(message) che viene quindi catturata nell'organizzazione. metodo springframework.beans.BeanUtils.instantiateClass(...).

Se si sceglie di generare un NPE e si utilizza l'argomento nel metodo, potrebbe essere ridondante e costoso verificare esplicitamente la presenza di un valore null.Penso che la VM lo faccia già per te.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top