Domanda Evitare! = Istruzioni nulle


Io uso object != null molto da evitare NullPointerException.

C'è una buona alternativa a questo?

Per esempio:

if (someobject != null) {
    someobject.doCalc();
}

Questo evita a NullPointerException, quando è sconosciuto se l'oggetto è null o no.

Si noti che la risposta accettata potrebbe non essere aggiornata, vedere https://stackoverflow.com/a/2386013/12943 per un approccio più recente.


3547


origine


risposte:


Questo per me sembra un problema abbastanza comune che gli sviluppatori di livello da junior a intermedio tendono ad affrontare ad un certo punto: o non sanno o non si fidano dei contratti a cui stanno partecipando e superano in modo difensivo i valori nulli. Inoltre, quando scrivono il proprio codice, tendono a fare affidamento sul restituire null per indicare qualcosa che richiede quindi al chiamante di verificare la presenza di null.

Per dirla in un altro modo, ci sono due casi in cui viene visualizzato il controllo nullo:

  1. Dove nullo è una risposta valida in termini di contratto; e

  2. Dove non è una risposta valida.

(2) è facile. O usare assert dichiarazioni (asserzioni) o consentire il fallimento (ad esempio, NullPointerException). Le asserzioni sono una funzionalità Java molto utilizzata che è stata aggiunta in 1.4. La sintassi è:

assert <condition>

o

assert <condition> : <object>

dove <condition> è un'espressione booleana e <object> è un oggetto di cui toString() l'output del metodo verrà incluso nell'errore.

Un assert la dichiarazione lancia un Error (AssertionError) se la condizione non è vera. Per impostazione predefinita, Java ignora le asserzioni. È possibile abilitare le asserzioni passando l'opzione -ea alla JVM. È possibile abilitare e disabilitare asserzioni per singole classi e pacchetti. Ciò significa che è possibile convalidare il codice con le asserzioni durante lo sviluppo e il test, e disabilitarle in un ambiente di produzione, anche se i miei test non hanno mostrato alcun impatto sulle prestazioni dalle affermazioni.

Non utilizzare le asserzioni in questo caso è OK perché il codice fallirà, che è ciò che accadrà se si usano asserzioni. L'unica differenza è che con le asserzioni potrebbe accadere prima, in un modo più significativo e possibilmente con informazioni aggiuntive, che potrebbero aiutarti a capire perché è successo se non ti aspettavi.

(1) è un po 'più difficile. Se non hai il controllo sul codice che stai chiamando, allora sei bloccato. Se null è una risposta valida, è necessario verificarla.

Se è il codice che controlli, tuttavia (e questo è spesso il caso), allora è una storia diversa. Evitare l'uso di null come risposta. Con metodi che restituiscono raccolte, è facile: restituisci collezioni vuote (o array) invece di null praticamente tutto il tempo.

Con le non-raccolte potrebbe essere più difficile. Consideralo come un esempio: se hai queste interfacce:

public interface Action {
  void doSomething();
}

public interface Parser {
  Action findAction(String userInput);
}

dove Parser prende l'input dell'utente grezzo e trova qualcosa da fare, forse se stai implementando un'interfaccia a riga di comando per qualcosa. Ora puoi fare in modo che il contratto restituisca null se non c'è un'azione appropriata. Questo porta al controllo nulla di cui stai parlando.

Una soluzione alternativa è non restituire mai null e utilizzare invece il Modello oggetto nullo:

public class MyParser implements Parser {
  private static Action DO_NOTHING = new Action() {
    public void doSomething() { /* do nothing */ }
  };

  public Action findAction(String userInput) {
    // ...
    if ( /* we can't find any actions */ ) {
      return DO_NOTHING;
    }
  }
}

Confrontare:

Parser parser = ParserFactory.getParser();
if (parser == null) {
  // now what?
  // this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
  // do nothing
} else {
  action.doSomething();
}

a

ParserFactory.getParser().findAction(someInput).doSomething();

che è un design molto migliore perché porta a un codice più conciso.

Detto questo, forse è del tutto appropriato che il metodo findAction () generi un'eccezione con un messaggio di errore significativo, specialmente in questo caso in cui si sta facendo affidamento sull'input dell'utente. Sarebbe molto meglio per il metodo findAction lanciare un'eccezione piuttosto che per il metodo di chiamata saltare in aria con una semplice NullPointerException senza spiegazione.

try {
    ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
    userConsole.err(anfe.getMessage());
}

O se pensi che il meccanismo try / catch sia troppo brutto, piuttosto che fare nulla, l'azione predefinita dovrebbe fornire un feedback all'utente.

public Action findAction(final String userInput) {
    /* Code to return requested Action if found */
    return new Action() {
        public void doSomething() {
            userConsole.err("Action not found: " + userInput);
        }
    }
}

2389



Se si utilizza (o si prevede di utilizzare) un IDE Java simile JetBrains IntelliJ IDEA, Eclipse o Netbeans o uno strumento come findbugs, quindi puoi usare le annotazioni per risolvere questo problema.

Fondamentalmente, hai @Nullable e @NotNull.

Puoi usare in metodo e parametri, come questo:

@NotNull public static String helloWorld() {
    return "Hello World";
}

o

@Nullable public static String helloWorld() {
    return "Hello World";
}

Il secondo esempio non verrà compilato (in IntelliJ IDEA).

Quando usi il primo helloWorld() funzione in un altro pezzo di codice:

public static void main(String[] args)
{
    String result = helloWorld();
    if(result != null) {
        System.out.println(result);
    }
}

Ora il compilatore IDEA IntelliJ ti dirà che il controllo è inutile, dal momento che helloWorld() la funzione non tornerà null, mai.

Utilizzando il parametro

void someMethod(@NotNull someParameter) { }

se scrivi qualcosa come:

someMethod(null);

Questo non verrà compilato.

Ultimo esempio usando @Nullable

@Nullable iWantToDestroyEverything() { return null; }

Facendo questo

iWantToDestroyEverything().something();

E puoi star certo che questo non accadrà. :)

È un buon modo per consentire al compilatore di controllare qualcosa di più di quello che fa di solito e per rafforzare i tuoi contratti. Sfortunatamente, non è supportato da tutti i compilatori.

IntelliJ IDEA 10.5 e on, hanno aggiunto il supporto per qualsiasi altro @Nullable  @NotNull implementazioni.

Vedi il post sul blog Altre annotazioni @ Nullable / @ NotNull più flessibili e configurabili.


516



Se i valori null non sono consentiti

Se il tuo metodo è chiamato esternamente, inizia con qualcosa di simile a questo:

public void method(Object object) {
  if (object == null) {
    throw new IllegalArgumentException("...");
  }

Quindi, nel resto del metodo, lo saprai object non è nullo

Se si tratta di un metodo interno (non parte di un'API), basta documentare che non può essere nullo, e il gioco è fatto.

Esempio:

public String getFirst3Chars(String text) {
  return text.subString(0, 3);
}

Tuttavia, se il tuo metodo passa semplicemente il valore su, e il metodo successivo lo passa ecc. Potrebbe diventare problematico. In tal caso, puoi controllare l'argomento come sopra.

Se nullo è permesso

Questo dipende davvero. Se trovi che faccio spesso qualcosa del genere:

if (object == null) {
  // something
} else {
  // something else
}

Quindi mi dirigo e faccio due cose completamente diverse. Non c'è brutto frammento di codice, perché ho davvero bisogno di fare due cose diverse a seconda dei dati. Ad esempio, dovrei lavorare sull'input o dovrei calcolare un buon valore predefinito?


In realtà è raro che io usi l'idioma "if (object != null && ...".

Potrebbe essere più facile darti degli esempi, se mostri esempi di dove usi in genere l'idioma.


282



Wow, quasi odio aggiungere un'altra risposta quando abbiamo 57 modi diversi di raccomandare il NullObject pattern, ma penso che alcune persone interessate a questa domanda possano piacere sapere che c'è una proposta sul tavolo per Java 7 da aggiungere "manipolazione nulla-sicura"-una sintassi semplificata per la logica if-not-equal-null.

L'esempio dato da Alex Miller assomiglia a questo:

public String getPostcode(Person person) {  
  return person?.getAddress()?.getPostcode();  
}  

Il ?. significa solo de-referenziare l'identificatore sinistro se non è nullo, altrimenti valuta il resto dell'espressione come null. Alcune persone, come il membro di Java Posse Dick Wall e il gli elettori di Devoxx amo davvero questa proposta, ma c'è anche opposizione, sulla base del fatto che in realtà incoraggerà maggiormente l'uso di null come valore sentinella.


Aggiornare: Un proposta ufficiale per un operatore null-safe in Java 7 è stato presentato sotto Progetto Coin. La sintassi è leggermente diversa dall'esempio sopra, ma è la stessa nozione.


Aggiornare: La proposta dell'operatore null-safe non è stata convertita in Project Coin. Quindi, non vedrai questa sintassi in Java 7.


215



Se i valori non definiti non sono consentiti:

È possibile configurare l'IDE per avvisare in merito a potenziali dereferenze nulle. Per esempio. in Eclipse, vedi Preferenze> Java> Compilatore> Errori / Avvisi / Analisi Null.

Se sono consentiti valori non definiti:

Se si desidera definire una nuova API in cui i valori non definiti hanno senso, Usa il Modello di opzione (potrebbe essere familiare dai linguaggi funzionali). Presenta i seguenti vantaggi:

  • È dichiarato esplicitamente nell'API se un input o output esiste o meno.
  • Il compilatore ti obbliga a gestire il caso "non definito".
  • L'opzione è una monade, quindi non c'è bisogno di un controllo null dettagliato, basta usare map / foreach / getOrElse o un combinatore simile per usare in sicurezza il valore (esempio).

Java 8 ha un built-in Optional classe (raccomandata); per le versioni precedenti, ci sono alternative di libreria, per esempio Guaiava'S Optional o FunctionalJava'S Option. Ma come molti modelli di stile funzionale, l'uso di Option in Java (anche 8) produce un certo numero di combinazioni, che è possibile ridurre utilizzando un linguaggio JVM meno dettagliato, ad es. Scala o Xtend.

Se devi gestire un'API che potrebbe restituire valori null, non puoi fare molto in Java. Xtend e Groovy hanno il Operatore Elvis  ?: e il operatore di dereferenze con sicurezza nulla  ?., ma si noti che questo restituisce null in caso di riferimento null, quindi semplicemente "rimuove" la corretta gestione di null.


177



Solo per questa situazione -

Non verificare se una variabile è nullo prima di richiamare un metodo equals (una stringa confronta l'esempio seguente):

if ( foo.equals("bar") ) {
 // ...
}

risulterà in a NullPointerException Se foo non esiste

Puoi evitarlo se confronti il ​​tuo Strings come questo:

if ( "bar".equals(foo) ) {
 // ...
}

160



Con Java 8 arriva il nuovo java.util.Optional classe che probabilmente risolve alcuni dei problemi. Si può almeno dire che migliora la leggibilità del codice e, nel caso di API pubbliche, rendere più chiaro il contratto dell'API allo sviluppatore del client.

Funzionano così:

Un oggetto opzionale per un determinato tipo (Fruit) viene creato come tipo di ritorno di un metodo. Può essere vuoto o contenere a Fruit oggetto:

public static Optional<Fruit> find(String name, List<Fruit> fruits) {
   for (Fruit fruit : fruits) {
      if (fruit.getName().equals(name)) {
         return Optional.of(fruit);
      }
   }
   return Optional.empty();
}

Ora guarda questo codice dove cerchiamo un elenco di Fruit (fruits) per una data istanza di Fruit:

Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
   Fruit fruit = found.get();
   String name = fruit.getName();
}

Puoi usare il map() operatore per eseguire un calcolo su - o estrarre un valore da - un oggetto opzionale. orElse() ti consente di fornire un fallback per i valori mancanti.

String nameOrNull = find("lemon", fruits)
    .map(f -> f.getName())
    .orElse("empty-name");

Naturalmente, il controllo del valore null / vuoto è ancora necessario, ma almeno lo sviluppatore è consapevole che il valore potrebbe essere vuoto e il rischio di dimenticare il controllo è limitato.

In un'API costruita da zero utilizzando Optional ogni volta che un valore di ritorno può essere vuoto e restituire un oggetto semplice solo quando non può essere null (convenzione), il codice client potrebbe abbandonare i controlli nulli sui valori di ritorno dell'oggetto semplice ...

Ovviamente Optional potrebbe anche essere usato come argomento del metodo, forse un modo migliore per indicare argomenti opzionali rispetto a 5 o 10 metodi di overload in alcuni casi.

Optional offre altri metodi convenienti, come ad esempio orElse che consentono l'uso di un valore predefinito, e ifPresent con cui funziona espressioni lambda.

Vi invito a leggere questo articolo (la mia fonte principale per scrivere questa risposta) in cui il NullPointerException (e in generale un puntatore nullo) problematico così come la (parziale) soluzione portata da Optional sono ben spiegati: Oggetti opzionali Java.


135



A seconda del tipo di oggetti che stai controllando, potresti essere in grado di utilizzare alcune delle classi dei comuni apache come: apache commons lang e apache commons collections

Esempio:

String foo;
...
if( StringUtils.isBlank( foo ) ) {
   ///do something
}

oppure (a seconda di cosa è necessario controllare):

String foo;
...
if( StringUtils.isEmpty( foo ) ) {
   ///do something
}

La classe StringUtils è solo una delle tante; ci sono alcune buone classi nei beni comuni che fanno manipolazioni nulle.

Segue un esempio di come è possibile utilizzare null vallidation in JAVA quando si include la libreria apache (commons-lang-2.4.jar)

public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {
    Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
    return read(new StringReader(xml), true, validationEventHandler);
}

E se usi Spring, Spring ha anche la stessa funzionalità nel suo pacchetto, vedi library (spring-2.4.6.jar)

Esempio su come usare questo statf classf da spring (org.springframework.util.Assert)

Assert.notNull(validationEventHandler,"ValidationHandler not Injected");

113



  • Se consideri che un oggetto non dovrebbe essere nullo (o è un bug) usa un assert.
  • Se il tuo metodo non accetta parametri null lo dici in javadoc e usa un assert.

Devi controllare per oggetto! = Null solo se vuoi gestire il caso in cui l'oggetto potrebbe essere nullo ...

C'è una proposta per aggiungere nuove annotazioni in Java7 per aiutare con parametri null / notnull: http://tech.puredanger.com/java7/#jsr308


87



Sono un fan del codice "fail fast". Chiediti: stai facendo qualcosa di utile nel caso in cui il parametro sia nullo? Se non hai una risposta chiara per ciò che il tuo codice dovrebbe fare in quel caso ... I.e. non dovrebbe mai essere nullo in primo luogo, quindi ignorarlo e consentire a una NullPointerException di essere lanciata. Il codice chiamante avrà lo stesso senso di un NPE come sarebbe un IllegalArgumentException, ma sarà più facile per lo sviluppatore eseguire il debug e capire cosa è andato storto se viene lanciato un NPE piuttosto che il codice che tenta di eseguire qualche altro imprevisto logica - che alla fine si traduce nell'errore dell'applicazione in ogni caso.


80