Il modo migliore per gestire i null in Java? [chiuso]

21

Ho un codice che non funziona a causa di NullPointerException. Un metodo viene chiamato sull'oggetto in cui l'oggetto non esiste.

Tuttavia, questo mi ha portato a pensare al modo migliore per risolvere questo problema. Eseguo sempre il codice difensivo per i valori null in modo che il codice di prova futuro per le eccezioni del puntatore nullo, o dovrei correggere la causa del null in modo che non si verifichi a valle.

Quali sono i tuoi pensieri?

    
posta Shaun F 23.02.2011 - 20:19
fonte

11 risposte

42

Se null è un parametro di input ragionevole per il metodo, correggere il metodo. In caso contrario, correggi il chiamante. "Reasonable" è un termine flessibile, quindi propongo il seguente test: come dovrebbe il metodo passare un input nullo? Se trovi più di una risposta possibile, allora null è non un input ragionevole.

    
risposta data 23.02.2011 - 20:32
fonte
19

Non utilizzare null, usa Facoltativo

Come hai notato, uno dei maggiori problemi con null in Java è che può essere usato ovunque , o almeno per tutti i tipi di riferimento.

È impossibile dire che potrebbe essere null e cosa non potrebbe essere.

Java 8 introduce uno schema molto migliore: Optional .

E esempio da Oracle:

String version = "UNKNOWN";
if(computer != null) {
  Soundcard soundcard = computer.getSoundcard();
  if(soundcard != null) {
    USB usb = soundcard.getUSB();
    if(usb != null) {
      version = usb.getVersion();
    }
  }
}

Se ognuno di questi valori può restituire o meno un valore corretto, puoi modificare le API in Optional s:

String name = computer.flatMap(Computer::getSoundcard)
    .flatMap(Soundcard::getUSB)
    .map(USB::getVersion)
    .orElse("UNKNOWN");

Codificando esplicitamente le opzioni nel tipo, le tue interfacce saranno molto migliori e il tuo codice sarà più pulito.

Se non stai utilizzando Java 8, puoi guardare com.google.common.base.Optional in Google Guava.

Una buona spiegazione del team di Guava: link

Una spiegazione più generale degli svantaggi di null, con esempi tratti da più lingue: link

@Nonnull, @Nullable

Java 8 aggiunge queste annotazioni per aiutare gli strumenti di controllo del codice come IDE a individuare problemi. Sono abbastanza limitati nella loro efficacia.

Controlla quando ha senso

Non scrivere il 50% del tuo codice che controlla null, in particolare se non c'è nulla di sensato che il tuo codice possa fare con un valore di null .

D'altra parte, se null può essere usato e significa qualcosa, assicurati di usarlo.

In definitiva, ovviamente non puoi rimuovere null da Java. Raccomando vivamente di sostituire l'astrazione Optional ogni volta che è possibile e di controllare null quelle altre volte in cui puoi fare qualcosa di ragionevole a riguardo.

    
risposta data 04.09.2015 - 00:57
fonte
8

Ci sono molti modi per gestire questo problema, il peppering del codice con if (obj != null) {} non è l'ideale, è caotico, aggiunge rumore durante la lettura del codice più avanti durante il ciclo di manutenzione ed è soggetto a errori, poiché è facile dimenticarlo fare questo avvolgimento della piastra della caldaia.

Dipende se vuoi che il codice continui ad essere eseguito in modo silenzioso o fallisca. null è un errore o una condizione prevista.

Che cos'è null

In ogni definizione e caso, Null rappresenta l'assoluta mancanza di dati. I valori null nei database rappresentano la mancanza di un valore per quella colonna. un String nullo non è lo stesso di un String vuoto, in teoria un int non è la stessa cosa di ZERO. In pratica "dipende". Vuoto String può fare una buona implementazione Null Object per la classe String, per Integer dipende dalla logica aziendale.

Le alternative sono:

  1. Il modello Null Object . Crea un'istanza del tuo oggetto che rappresenta lo stato null e inizializza tutti i riferimenti a quel tipo con un riferimento all'implementazione Null . Questo è utile per oggetti di tipo valore semplice che non hanno molti riferimenti ad altri oggetti che potrebbero anche essere null e dovrebbe essere null come stato valido.

  2. Usa gli strumenti Orientamento orientato per tessere metodi con un aspetto Null Checker che impedisce ai parametri di essere nulli. Questo è per i casi in cui null è un errore.

  3. Usa assert() non molto meglio di if (obj != null){} ma meno rumore.

  4. Utilizza uno strumento di applicazione del contratto come Contratti per Java . Lo stesso caso d'uso è simile a AspectJ ma è più recente e utilizza Annotations invece dei file di configurazione esterni. Meglio di entrambi i lavori di Aspects and Asserts.

1 è la soluzione ideale quando i dati in ingresso sono noti per essere null e devono essere sostituiti con un valore predefinito in modo che i consumatori a monte non debbano occuparsi di tutto il codice di codice di verifica null. Anche il controllo su valori predefiniti noti sarà più espressivo.

2, 3 e 4 sono solo convenienti generatori di eccezioni alternative per sostituire NullPointerException con qualcosa di più informativo, che è sempre e miglioramento.

Alla fine

null in Java è quasi sempre un errore logico. Dovresti sempre cercare di eliminare la causa principale di NullPointerExceptions . Dovresti cercare di non utilizzare null condizioni come logica aziendale. if (x == null) { i = someDefault; } crea solo l'asserzione iniziale a quell'istanza di oggetto predefinita.

    
risposta data 23.02.2011 - 21:02
fonte
7

L'aggiunta di verifiche nulle potrebbe rendere problematico il test. Vedi questa grande lezione ...

Dai un'occhiata alla conferenza di Google Tech: "Il codice pulito parla - non cercare le cose!" ne parla intorno al minuto 24

link

La programmazione paranoide comporta l'aggiunta di controlli null ovunque. Sembra una buona idea in un primo momento, tuttavia, da una prospettiva di test, rende difficile il controllo del tipo di controllo nullo.

Inoltre, quando crei una precondizione per l'esistenza di qualche oggetto come

class House(Door door){

    .. null check here and throw exception if Door is NULL
    this.door = door
}

ti impedisce di creare House dal momento che genererai un'eccezione. Supponiamo che i casi di test stiano creando oggetti mock per testare qualcosa di diverso da Door, beh, non puoi farlo perché è richiesta la porta

Coloro che hanno sofferto attraverso l'inferno della creazione di Mock sono ben consapevoli di questi tipi di fastidi.

In breve, la tua suite di test dovrebbe essere abbastanza robusta da poter essere testata per porte, case, tetti o qualsiasi altra cosa senza dover essere paranoico al riguardo. Seroiusly, quanto è difficile aggiungere un test di verifica nulla per oggetti specifici nei tuoi test :)

Dovresti sempre preferire le applicazioni che funzionano perché hai diversi test che PROVE funziona, piuttosto che HOPING funziona semplicemente perché hai un sacco di controlli null precondizionati dappertutto

    
risposta data 05.08.2013 - 19:33
fonte
4

tl; dr - È BUONO controllare l'inaspettato null s ma BAD per un'applicazione per cercare di renderlo buono.

Dettagli

Chiaramente, ci sono situazioni in cui null è un input o output valido per un metodo e altri dove non lo è.

Regola n. 1:

The javadoc for a method that allows a null parameter or returns a null value must clearly document this, and explain what the null means.

Regola n. 2:

An API method should not be specified as accepting or returning null unless there is a good reason to do so.

Dato una chiara specificazione del "contratto" di un metodo rispetto al null s, è un errore di programmazione per passare o restituire un null dove non dovresti.

Regola n. 3:

An application should not attempt to "make good" programming errors.

Se un metodo rileva un null che non dovrebbe essere presente, non dovrebbe tentare di risolvere il problema trasformandolo in qualcos'altro. Questo nasconde il problema al programmatore. Invece, dovrebbe consentire che l'NPE si verifichi e causare un errore in modo che il programmatore possa capire quale sia la causa principale e risolverlo. Speriamo che il guasto verrà notato durante i test. Altrimenti, questo dice qualcosa sulla tua metodologia di test.

Regola n. 4:

Where feasible, write your code to detect programming errors early.

Se nel tuo codice ci sono errori che portano a molti NPE, la cosa più difficile può essere capire da dove provengono i valori di null . Un modo per semplificare la diagnosi è scrivere il codice in modo che il null venga rilevato il prima possibile. Spesso puoi farlo in combinazione con altri assegni; per es.

public setName(String name) {
    // This also detects 'null' as an (intended) side-effect
    if (name.length() == 0) {
        throw new IllegalArgumentException("empty name");
    }
}

(Ci sono ovviamente casi in cui le regole 3 e 4 dovrebbero essere attenuate con la realtà. Ad esempio (regola 3), alcuni tipi di applicazioni devono tentare di continuare dopo aver rilevato probabilmente errori di programmazione. 4) il controllo eccessivo dei parametri errati può avere un impatto sulle prestazioni.)

    
risposta data 24.02.2011 - 00:01
fonte
3

Suggerirei di fissare il metodo per essere difensivo. Ad esempio:

String go(String s){  
    return s.toString();  
}

Dovrebbe essere più simile a questo:

String go(String s){  
    if(s == null){  
       return "";  
    }     
    return s.toString();  
}

Mi rendo conto che questo è assolutamente banale, ma se l'invocatore si aspetta che un oggetto dia loro un oggetto predefinito che non causerà il passaggio di un nulla.

    
risposta data 23.02.2011 - 20:37
fonte
2

Le seguenti regole generali su null mi hanno aiutato molto finora:

  1. Se i dati provengono dall'esterno del tuo controllo, controlla sistematicamente i valori nulli e agisci in modo appropriato. Ciò significa lanciare un'eccezione che ha senso per la funzione (selezionata o deselezionata, assicurati solo che il nome dell'eccezione ti dica esattamente cosa sta succedendo). MAI, MAI, per non perdere un valore nel tuo sistema che può potenzialmente portare sorprese.

  2. Se Null rientra nel dominio dei valori appropriati per il tuo modello dati, gestiscilo appropriatamente.

  3. Quando si restituiscono valori, provare a non restituire valori null quando possibile. Preferisci sempre liste vuote, stringhe vuote, modelli oggetto nulli. Mantieni valori nulli come valori restituiti quando questa è la migliore rappresentazione possibile dei dati per un determinato caso d'uso.

  4. Probabilmente il più importante di tutti ... Test, prove e test di nuovo. Quando testate il vostro codice non testarlo come un programmatore testarlo come una dominatrice psicopatica nazista e cercare di immaginare tutti i modi per torturare il diavolo da quel codice.

Questo tende un po 'al lato paranoico dei null che spesso portano a facciate e proxy che interfacciano sistemi al mondo esterno e valori strettamente controllati all'interno con una ridondanza abbondante. Il mondo esterno qui significa praticamente tutto ciò che non ho codificato. Sopporta un runtime di costo ma finora ho raramente dovuto ottimizzare questo creando "sezioni di sicurezza nulle" del codice. Devo dire però che per lo più creo sistemi a lunga durata per l'assistenza sanitaria e l'ultima cosa che voglio è che il sottosistema di interfaccia che porta le allergie iodio allo scanner CT si blocchi a causa di un puntatore nullo imprevisto perché qualcun altro in un altro sistema non ha mai capito che i nomi potevano contiene apostrofi o caratteri come 但 耒耨.

comunque ... i miei 2 centesimi

    
risposta data 24.02.2011 - 03:13
fonte
1

Proporrei di usare il modello Option / Some / None dai linguaggi funzionali. Non sono uno specialista di Java, ma uso intensamente la mia implementazione di questo pattern nel mio progetto C #, e sono sicuro che potrebbe essere convertito in mondo Java.

L'idea alla base di questo schema è la seguente: se è logico avere una situazione in cui vi è la possibilità di assenza di un valore (ad esempio quando si recupera dal database con id) si fornisce un oggetto di tipo Opzione [T], dove T è un valore possibile. I caso di assenza di un oggetto valore della classe None [T] restituito, nel caso in cui l'esistenza del valore - oggetto di Some [T] restituito, contenente il valore.

In questo caso, deve gestire la possibilità di assenza di valore e, se esegui la revisione del codice, potresti facilmente individuare una gestione errata. Per trarre ispirazione dall'implementazione del linguaggio C #, consultare il mio repository bitbucket link

Se si restituisce un valore nullo ed è logicamente equivalente a un errore (ad esempio nessun file esiste o non è in grado di connettersi) si dovrebbe generare un'eccezione o utilizzare un altro modello di gestione degli errori. L'idea alla base di questo, ancora una volta, si risolve con la soluzione, quando deve gestire la situazione di assenza di valore, e potrebbe facilmente trovare le posizioni di gestione errata nel codice.

    
risposta data 05.08.2013 - 20:07
fonte
0

Beh, se uno dei possibili risultati del tuo metodo è un valore nullo, dovresti codificarlo in modo difensivo, ma se un metodo dovrebbe restituire un valore non nullo ma non lo risolverei di sicuro.

Come al solito dipende dal caso, come con la maggior parte delle cose nella vita:)

    
risposta data 23.02.2011 - 20:23
fonte
0

Le librerie lang di Apache Commons forniscono un modo per gestire valori nulli

il metodo defaultIfNull nella classe ObjectUtils consente di restituire un valore predefinito se l'oggetto passato è nullo

    
risposta data 24.02.2011 - 00:19
fonte
0
  1. Non utilizzare il tipo Optional, a meno che non sia veramente facoltativo, spesso l'uscita viene gestita meglio come eccezione, a meno che tu non stessi aspettando null come opzione, e non perché scrivi regolarmente codice bug.

  2. Il problema non è nullo come tipo che ha i suoi usi, come sottolinea l'articolo di Google. Il problema è che i valori nulli dovrebbero essere controllati e gestiti, spesso possono essere chiusi con grazia.

  3. Esistono numerosi casi null che rappresentano condizioni non valide in aree esterne all'operazione di routine del programma (input utente non valido, problemi di database, errore di rete, file mancanti, dati corrotti), che è ciò che le eccezioni controllate sono per, gestendoli, anche se è solo per registrarlo.

  4. La gestione delle eccezioni è permessa diverse priorità operative e privilegi nella JVM rispetto a un tipo, come Opzionale, che ha senso per la natura eccezionale della loro occorrenza, incluso il supporto iniziale per il caricamento pigro prioritario del gestore in memoria , dal momento che non ne hai bisogno in giro tutto il tempo.

  5. Non è necessario scrivere gestori di null dappertutto, solo dove è probabile che si verifichino, ovunque si acceda potenzialmente a servizi di dati inaffidabili, e poiché la maggior parte di questi si riduce a pochi schemi generali che possono facilmente essere astratto hai solo bisogno di una chiamata gestore, tranne in rari casi.

Quindi suppongo che la mia risposta sarebbe racchiuderla in un'eccezione controllata e gestirla, o correggere il codice inaffidabile se è nella tua capacità.

    
risposta data 05.09.2015 - 08:09
fonte

Leggi altre domande sui tag