it-swarm.it

Evitare! = Istruzioni nulle

Io uso object != null molto per evitare NullPointerException .

C'è una buona alternativa a questo? 

Per esempio:

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

Questo evita un NullPointerException, quando è sconosciuto se l'oggetto è null oppure no.

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

3729
Goran Martinic

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 controllano 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. Utilizzare le istruzioni assert (asserzioni) o consentire l'errore (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 il cui output del metodo toString() sarà incluso nell'errore.

Un'istruzione assert genera 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à, il 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 extra, 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 i metodi che restituiscono le raccolte, è semplice: 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 pattern Null Object :

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 generare un'eccezione piuttosto che il metodo di chiamata per far saltare in aria 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);
        }
    }
}
2487
cletus

Se usi (o stai progettando di usare) un Java IDE come JetBrains IntelliJ IDEA , Eclipse o Netbeans o uno strumento come findbugs allora puoi usare annotazioni 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 si utilizza la prima funzione helloWorld() in un'altra parte di codice:

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

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

Utilizzando il parametro

void someMethod(@NotNull someParameter) { }

se scrivi qualcosa come:

someMethod(null);

Questo non verrà compilato.

Ultimo esempio con @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.

In IntelliJ IDEA 10.5 e su, hanno aggiunto il supporto per qualsiasi altra implementazione di @Nullable@NotNull.

Vedi post del blog Più flessibile e configurabile @ Nullable/@ NotNull annotazioni.

559
Luca Molteni

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, saprai che 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 solo il valore, 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'espressione "if (object != null && ...".

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

296
myplacedk

Wow, odio quasi aggiungere un'altra risposta quando abbiamo 57 modi diversi per raccomandare NullObject pattern, ma penso che alcune persone interessate a questa domanda possano piacere sapere che c'è una proposta sulla tabella per Java 7 da aggiungere " gestione null-safe " - 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 gli gli elettori di Devoxx amano davvero questa proposta, ma c'è anche un'opposizione, sulla base del fatto che in realtà incoraggerà maggiormente l'uso di null come valore sentinella.


Aggiornamento: Una proposta ufficiale per un operatore null-safe in Java 7 è stata inviata sotto Progetto Coin. La sintassi è leggermente diversa dall'esempio sopra, ma è la stessa nozione.


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

222
erickson

Se i valori non definiti non sono consentiti:

Potresti configurare il tuo IDE per avvisarti di potenziali dereferenze nulle. Per esempio. in Eclipse, vedi Preferenze> Java> Compilatore> Errori/Avvisi/Analisi Null.

Se sono consentiti valori non definiti:

Se vuoi definire una nuova API in cui i valori non definiti hanno senso, usa Option Pattern (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".
  • Option is a monad , quindi non c'è bisogno di un controllo null dettagliato, basta usare map/foreach/getOrElse o un combinatore simile per usare tranquillamente il valore (esempio) .

Java 8 ha una classe Optional integrata (consigliata); per le versioni precedenti, esistono alternative di libreria, ad esempio Guava ' Optional o FunctionalJava ' s Option . Ma come molti modelli di stile funzionale, l'uso di Option in Java (anche 8) produce un po 'di codice, che puoi ridurre usando un linguaggio JVM meno dettagliato, ad es. Scala o Xtend.

Se devi gestire un'API che potrebbe restituire null, non puoi fare molto in Java. Xtend e Groovy hanno l'operatore Elvis?: e l'operatore dereferenziazione null-safe?., ma si noti che questo restituisce null in caso di riferimento null, quindi "rimanda" la corretta gestione di null.

184
thSoft

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 un NullPointerException se foo non esiste.

Puoi evitarlo se confronti il ​​tuo Strings in questo modo:

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

Con Java 8 arriva la nuova classe Java.util.Optional 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 facoltativo per un determinato tipo (Fruit) viene creato come tipo di ritorno di un metodo. Può essere vuoto o contenere un oggetto Fruit:

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 una lista 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();
}

È possibile utilizzare l'operatore map() per eseguire un calcolo su - o estrarre un valore da - un oggetto facoltativo. orElse() 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 restituito può essere vuoto e restituendo un oggetto semplice solo quando non può essere null (convenzione), il codice client potrebbe abbandonare i controlli Null 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 orElse che consentono l'uso di un valore predefinito, e ifPresent che funziona con espressioni lambda .

Vi invito a leggere questo articolo (la mia fonte principale per scrivere questa risposta) in cui il problema NullPointerException (e in generale il puntatore nullo) e la soluzione (parziale) di Optional sono ben spiegati: Oggetti opzionali Java.

148
Pierre Henry

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

Esempio: 

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

oppure (a seconda di cosa è necessario verificare):

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 stai usando 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");
119
javamonkey79
  • 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

91
pgras

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 la proiezione di NullPointerException. 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. 

85
Alex Worden

Il framework delle raccolte Google offre un modo valido ed elegante per ottenere il controllo nulla.

C'è un metodo in una classe di libreria come questa:

static <T> T checkNotNull(T e) {
   if (e == null) {
      throw new NullPointerException();
   }
   return e;
}

E l'utilizzo è (con import static):

...
void foo(int a, Person p) {
   if (checkNotNull(p).getAge() > a) {
      ...
   }
   else {
      ...
   }
}
...

O nel tuo esempio:

checkNotNull(someobject).doCalc();
72
user2427

A volte, hai metodi che operano sui suoi parametri che definiscono un'operazione simmetrica:

a.f(b); <-> b.f(a);

Se sai che b non può mai essere nullo, puoi semplicemente cambiarlo. È molto utile per gli uguali: Invece di foo.equals("bar"); meglio fare "bar".equals(foo);.

Piuttosto che Null Object Pattern - che ha i suoi usi - potresti considerare le situazioni in cui l'oggetto nullo è un bug.

Quando viene lanciata l'eccezione, esamina la traccia dello stack e controlla il bug.

69
Jim Nelson

Java 7 ha una nuova classe di utilità Java.util.Objects su cui esiste un metodo requireNonNull(). Tutto ciò che fa è lanciare NullPointerException se il suo argomento è nullo, ma ripulisce un po 'il codice. Esempio:

Objects.requireNonNull(someObject);
someObject.doCalc();

Il metodo è molto utile per checking poco prima di un assegnamento in un costruttore, in cui ogni suo utilizzo può salvare tre righe di codice:

Parent(Child child) {
   if (child == null) {
      throw new NullPointerException("child");
   }
   this.child = child;
}

diventa

Parent(Child child) {
   this.child = Objects.requireNonNull(child, "child");
}
66
Stuart Marks

Null non è un "problema". È parte integrante di un set di strumenti di complete modeling. Il software mira a modellare la complessità del mondo e nullo porta il suo fardello. Nullo indica "Nessun dato" o "Sconosciuto" in Java e simili. Quindi è appropriato utilizzare null per questi scopi. Non preferisco il pattern 'Null object'; Penso che sorga il ' che custodirà I guardiani ' problema. 
Se mi chiedi qual è il nome della mia ragazza ti dirò che non ho una ragazza. Nel linguaggio Java restituirò null. Un'alternativa sarebbe quella di lanciare un'eccezione significativa per indicare alcuni problemi che non possono essere (o non vogliono essere) risolti lì e delegarli da qualche parte più in alto nello stack per riprovare o riportare l'errore di accesso ai dati all'utente . 

  1. Per una 'domanda sconosciuta' dare 'risposta sconosciuta'. (Essere nullo sicuro se ciò è corretto dal punto di vista del business) Controllare gli argomenti per null una volta all'interno di un metodo prima dell'utilizzo, sollevando più chiamanti dal verificarli prima di una chiamata.

    public Photo getPhotoOfThePerson(Person person) {
        if (person == null)
            return null;
        // Grabbing some resources or intensive calculation
        // using person object anyhow.
    }
    

    Precedente porta al normale flusso logico per non ottenere foto di una ragazza inesistente dalla mia libreria fotografica.

    getPhotoOfThePerson(me.getGirlfriend())
    

    E si adatta alla nuova API Java in arrivo (guardando avanti)

    getPhotoByName(me.getGirlfriend()?.getName())
    

    Mentre è piuttosto 'normale flusso aziendale' non trovare la foto memorizzata nel DB per qualche persona, usavo le coppie come sotto per altri casi

    public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException
    public static MyEnum parseMyEnumOrNull(String value);
    

    E non detestare digitare <alt> + <shift> + <j> (genera javadoc in Eclipse) e scrivere tre parole aggiuntive per la tua API pubblica. Questo sarà più che sufficiente per tutti tranne quelli che non leggono la documentazione.

    /**
     * @return photo or null
     */
    

    o

    /**
     * @return photo, never null
     */
    
  2. Questo è un caso piuttosto teorico e nella maggior parte dei casi si dovrebbe preferire l'API Java null safe (nel caso in cui venga rilasciata in altri 10 anni), ma NullPointerException è una sottoclasse di un Exception. Quindi è una forma di Throwable che indica le condizioni che un'applicazione ragionevole potrebbe voler rilevare ( javadoc )! Per utilizzare il primo vantaggio in termini di eccezioni e codice di gestione degli errori separato dal codice "normale" ( secondo i creatori di Java ) è opportuno, per quanto mi riguarda, rilevare NullPointerException.

    public Photo getGirlfriendPhoto() {
        try {
            return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());
        } catch (NullPointerException e) {
            return null;
        }
    }
    

    Le domande potrebbero sorgere:

    D. Cosa succede se getPhotoDataSource() restituisce null?
    UN. Spetta alla logica aziendale. Se non riesco a trovare un album fotografico, non ti mostrerò nessuna foto. Cosa succede se appContext non è inizializzato? La logica di business di questo metodo lo sopporta. Se la stessa logica deve essere più rigida, lanciare un'eccezione fa parte della logica aziendale e deve essere usato il controllo esplicito di null (caso 3). La nuova API Java Null-safe si adatta meglio qui per specificare in modo selettivo cosa implica e cosa non implica l'inizializzazione per essere veloce in caso di errori del programmatore.

    D. Il codice ridondante potrebbe essere eseguito e potrebbero essere prese risorse non necessarie.
    UN. Potrebbe aver luogo se getPhotoByName() provasse ad aprire una connessione al database, creare PreparedStatement e utilizzare il nome della persona come parametro SQL alla fine. L'approccio per una domanda sconosciuta dà una risposta sconosciuta (caso 1) funziona qui. Prima di afferrare le risorse, il metodo dovrebbe controllare i parametri e restituire il risultato 'sconosciuto' se necessario.

    D. Questo approccio ha una penalizzazione delle prestazioni dovuta all'apertura della prova.
    UN. Il software dovrebbe essere facile da capire e modificare in primo luogo. Solo dopo questo, si potrebbe pensare alle prestazioni e solo se necessario! e dove necessario! ( source ) e molti altri).

    PS. Questo approccio sarà ragionevole da usare come codice di gestione degli errori separato dal codice "normale" principio è ragionevole da usare in qualche luogo. Considera il prossimo esempio:

    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        try {
            Result1 result1 = performSomeCalculation(predicate);
            Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
            Result3 result3 = performThirdCalculation(result2.getSomeProperty());
            Result4 result4 = performLastCalculation(result3.getSomeProperty());
            return result4.getSomeProperty();
        } catch (NullPointerException e) {
            return null;
        }
    }
    
    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        SomeValue result = null;
        if (predicate != null) {
            Result1 result1 = performSomeCalculation(predicate);
            if (result1 != null && result1.getSomeProperty() != null) {
                Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
                if (result2 != null && result2.getSomeProperty() != null) {
                    Result3 result3 = performThirdCalculation(result2.getSomeProperty());
                    if (result3 != null && result3.getSomeProperty() != null) {
                        Result4 result4 = performLastCalculation(result3.getSomeProperty());
                        if (result4 != null) {
                            result = result4.getSomeProperty();
                        }
                    }
                }
            }
        }
        return result;
    }
    

    PPS. Per quelli veloci a downvote (e non così veloci da leggere la documentazione) vorrei dire che non ho mai preso un'eccezione a puntatore nullo (NPE) nella mia vita. Ma questa possibilità era intenzionalmente progettata dai creatori di Java perché NPE è una sottoclasse di Exception. Abbiamo un precedente nella cronologia di Java quando ThreadDeath è un Error non perché si tratta in realtà di un errore dell'applicazione, ma solo perché non era destinato a essere catturato! Quanto NPE si adatta ad essere un Error di ThreadDeath! Ma non è.

  3. Controlla "Nessun dato" solo se la logica aziendale lo implica.

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person != null) {
            person.setPhoneNumber(phoneNumber);
            dataSource.updatePerson(person);
        } else {
            Person = new Person(personId);
            person.setPhoneNumber(phoneNumber);
            dataSource.insertPerson(person);
        }
    }
    

    e

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person == null)
            throw new SomeReasonableUserException("What are you thinking about ???");
        person.setPhoneNumber(phoneNumber);
        dataSource.updatePerson(person);
    }
    

    Se appContext o dataSource non sono inizializzati runtime non gestito NullPointerException interromperà il thread corrente e verrà elaborato da Thread.defaultUncaughtExceptionHandler (per definire e utilizzare il logger o altro meccanismo di notifica preferito). Se non impostato, ThreadGroup # uncaughtException stamperà stacktrace su system err. Si dovrebbe monitorare il log degli errori dell'applicazione e aprire il problema di Jira per ogni eccezione non gestita che in realtà è un errore dell'applicazione. Il programmatore dovrebbe correggere il bug da qualche parte nelle cose di inizializzazione.

65

In definitiva, l'unico modo per risolvere completamente questo problema è utilizzare un linguaggio di programmazione diverso:

  • In Objective-C, puoi fare l'equivalente di invocare un metodo su nil, e assolutamente non succederà nulla. Questo rende superflua la maggior parte dei controlli nulli, ma può rendere gli errori molto più difficili da diagnosticare.
  • In Nice , un linguaggio derivato da Java, ci sono due versioni di tutti i tipi: una versione potenzialmente nulla e una versione non nulla. Puoi solo invocare metodi su tipi non nulli. I tipi potenzialmente null possono essere convertiti in tipi non nulli tramite il controllo esplicito di null. Questo rende molto più facile sapere dove sono necessari i controlli nulli e dove non lo sono.
48

"Problema" comune in Java davvero.

Innanzitutto, i miei pensieri su questo:

Considero che è male "mangiare" qualcosa quando è stato passato NULL dove NULL non è un valore valido. Se non stai uscendo dal metodo con una sorta di errore, significa che nel tuo metodo non è successo niente che non sia vero. Quindi, in questo caso, probabilmente restituisci null, e nel metodo di ricezione controlli nuovamente null, e non finisce mai, e finisci con "if! = Null", ecc.

Quindi, IMHO, null deve essere un errore critico che impedisce l'ulteriore esecuzione (ovvero, dove null non è un valore valido).

Il modo in cui risolvo questo problema è questo:

Innanzitutto, seguo questa convenzione:

  1. Tutti i metodi/API pubblici controllano sempre gli argomenti per null
  2. Tutti i metodi privati ​​non controllano nulla poiché sono metodi controllati (basta lasciarli morire con eccezione di nullpointer nel caso in cui non sia stato gestito in precedenza)
  3. Gli unici altri metodi che non verificano il null sono i metodi di utilità. Sono pubblici, ma se li chiami per qualche motivo, sai quali parametri passi. È come cercare di far bollire l'acqua nel bollitore senza fornire acqua ...

E infine, nel codice, la prima riga del metodo pubblico va così:

ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();

Si noti che addParam () restituisce sé stesso, in modo da poter aggiungere altri parametri da verificare.

Il metodo validate() farà comparire ValidationException se uno dei parametri è nullo (selezionato o deselezionato è più un problema di design/gusto, ma il mio ValidationException è spuntato).

void validate() throws ValidationException;

Il messaggio conterrà il seguente testo se, ad esempio, "plans" è nullo:

" Il valore dell'argomento non valido viene rilevato per il parametro [plans] "

Come puoi vedere, il secondo valore nel metodo addParam () (stringa) è necessario per il messaggio utente, perché non puoi facilmente rilevare il nome della variabile passata, anche con la reflection (non oggetto di questo post comunque ...).

E sì, sappiamo che oltre questa linea non incontreremo più un valore nullo, quindi invochiamo semplicemente metodi su quegli oggetti.

In questo modo, il codice è pulito, facile manutenibile e leggibile.

34
Oleg

Ponendo la domanda in questione, potresti essere interessato alle strategie di gestione degli errori. L'architetto del tuo team dovrebbe decidere come lavorare gli errori. Ci sono diversi modi per farlo:

  1. consentire alle eccezioni di ripple - catturarli nel 'ciclo principale' o in qualche altra routine di gestione.

    • controllare le condizioni di errore e gestirle in modo appropriato

Certo, guarda anche Aspect Oriented Programming: hanno modi perfetti per inserire if( o == null ) handleNull() nel tuo bytecode.

32
xtofl

Oltre a usare assert puoi usare quanto segue:

if (someobject == null) {
    // Handle null here then move on.
}

Questo è leggermente migliore di:

if (someobject != null) {
    .....
    .....



    .....
}
32
fastcodejava

Basta non usare mai null. Non permetterlo.

Nelle mie classi, la maggior parte dei campi e variabili locali hanno valori di default non nulli, e aggiungo dichiarazioni di contratto (sempre-on assert) ovunque nel codice per assicurarmi che questo sia applicato (dal momento che è più succinto e più espressivo che lasciarlo apparire come un NPE e quindi dover risolvere il numero di linea, ecc.).

Una volta adottata questa pratica, ho notato che i problemi sembravano risolversi da soli. Vedrai le cose molto prima nel processo di sviluppo per caso e ti rendi conto di avere un punto debole ... e soprattutto ... aiuta a incapsulare le preoccupazioni dei diversi moduli, i diversi moduli possono "fidarsi" l'un l'altro, e non più sporcare il codice con costrutti if = null else!

Questa è una programmazione difensiva e produce un codice molto più pulito a lungo termine. Disinfetta sempre i dati, ad es. qui imponendo standard rigidi, e i problemi vanno via.

class C {
    private final MyType mustBeSet;
    public C(MyType mything) {
       mustBeSet=Contract.notNull(mything);
    }
   private String name = "<unknown>";
   public void setName(String s) {
      name = Contract.notNull(s);
   }
}


class Contract {
    public static <T> T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; }
}

I contratti sono come test di mini-unità che sono sempre in esecuzione, anche in produzione, e quando le cose falliscono, sai perché, piuttosto che un NPE casuale che devi in ​​qualche modo capire.

30
ianpojman

Guava, una libreria di base molto utile di Google, ha un'API piacevole e utile per evitare i null. Trovo UsingAndAvoidingNullExplained molto utile.

Come spiegato nella wiki:

Optional<T> è un modo per sostituire un riferimento T nullable con un valore non nullo. Un facoltativo può contenere un riferimento T non nullo (nel qual caso diciamo che il riferimento è "presente"), o può contenere nulla (nel qual caso diciamo che il riferimento è "assente"). Non è mai ha detto di "contenere null".

Uso:

Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5
28

Questo è un problema molto comune per ogni sviluppatore Java. Quindi c'è un supporto ufficiale in Java 8 per risolvere questi problemi senza codice ingombrante.

Java 8 ha introdotto Java.util.Optional<T>. È un contenitore che può o non può contenere un valore non nullo. Java 8 ha fornito un modo più sicuro per gestire un oggetto il cui valore potrebbe essere nullo in alcuni casi. È ispirato dalle idee di Haskell e Scala .

In breve, la classe facoltativa include metodi per trattare esplicitamente i casi in cui un valore è presente o assente. Tuttavia, il vantaggio rispetto ai riferimenti null è che la classe <T> facoltativa ti costringe a pensare al caso in cui il valore non è presente. Di conseguenza, è possibile evitare eccezioni di puntatore nullo non intenzionale.

Nell'esempio sopra abbiamo una fabbrica di servizi a domicilio che restituisce un handle a più appliance disponibili in casa. Ma questi servizi possono o non possono essere disponibili/funzionali; significa che potrebbe risultare in una NullPointerException. Anziché aggiungere una condizione if nulla prima di utilizzare qualsiasi servizio, racchiudilo in Facoltativo <Servizio>.

CONFEZIONAMENTO OPZIONALE <T>

Consideriamo un metodo per ottenere un riferimento di un servizio da una fabbrica. Invece di restituire il riferimento del servizio, avvolgere con Opzionale. Permette all'utilizzatore dell'API di sapere che il servizio restituito può o meno essere disponibile/funzionale, utilizzare in modo difensivo

public Optional<Service> getRefrigertorControl() {
      Service s = new  RefrigeratorService();
       //...
      return Optional.ofNullable(s);
   }

Come vedi Optional.ofNullable() fornisce un modo semplice per ottenere il riferimento avvolto. Esistono altri modi per ottenere il riferimento di Opzionale, sia Optional.empty() che Optional.of(). Uno per restituire un oggetto vuoto invece di risintonizzare null e l'altro per avvolgere un oggetto non annullabile, rispettivamente.

COSÌ COME ESATTAMENTE AIUTA PER EVITARE UN CONTROLLO NULL?

Dopo aver avvolto un oggetto di riferimento, Opzionale fornisce molti metodi utili per invocare metodi su un riferimento fascicolato senza NPE.

Optional ref = homeServices.getRefrigertorControl();
ref.ifPresent(HomeServices::switchItOn);

Optional.ifPresent richiama il Consumatore dato con un riferimento se è un valore non nullo. Altrimenti, non fa nulla.

@FunctionalInterface
public interface Consumer<T>

Rappresenta un'operazione che accetta un singolo argomento di input e non restituisce alcun risultato. A differenza della maggior parte delle altre interfacce funzionali, ci si aspetta che il consumatore operi tramite effetti collaterali . È così pulito e facile da capire. Nell'esempio di codice sopra riportato, HomeService.switchOn(Service) viene richiamato se il riferimento di holding facoltativo non è nullo.

Usiamo molto spesso l'operatore ternario per verificare la condizione nulla e restituire un valore alternativo o un valore predefinito. Facoltativo fornisce un altro modo per gestire la stessa condizione senza controllare null. Optional.orElse (defaultObj) restituisce defaultObj se Opzionale ha un valore nullo. Usiamo questo nel nostro codice di esempio:

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

Ora HomeServices.get () fa la stessa cosa, ma in un modo migliore. Controlla se il servizio è già stato inizializzato di no. Se lo è, restituire lo stesso o creare un nuovo nuovo servizio. Facoltativo <T> .orElse (T) consente di restituire un valore predefinito.

Infine, ecco il nostro NPE e il codice null check-free:

import Java.util.Optional;
public class HomeServices {
    private static final int NOW = 0;
    private static Optional<HomeServices> service;

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

public Optional<Service> getRefrigertorControl() {
    Service s = new  RefrigeratorService();
    //...
    return Optional.ofNullable(s);
}

public static void main(String[] args) {
    /* Get Home Services handle */
    Optional<HomeServices> homeServices = HomeServices.get();
    if(homeServices != null) {
        Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
        refrigertorControl.ifPresent(HomeServices::switchItOn);
    }
}

public static void switchItOn(Service s){
         //...
    }
}

Il post completo è NPE e il codice Null check-free ... Davvero?.

21
Yogesh Devatraj

Mi piacciono gli articoli di Nat Pryce. Ecco i link:

Negli articoli c'è anche un link a un repository Git per un Java Maybe Type che trovo interessante, ma non penso che da solo possa diminuire il Controllando il codice bloat. Dopo aver fatto qualche ricerca su Internet, penso che il codice ! = Null bloat potrebbe essere diminuito principalmente da un'attenta progettazione.

20
Mr Palo

Ho provato NullObjectPattern ma per me non è sempre il modo migliore per andare. Ci sono volte quando una "nessuna azione" non è appropriata.

NullPointerException è un'eccezione Runtime che significa che è colpa degli sviluppatori e con sufficiente esperienza ti dice esattamente dove si trova l'errore.

Ora alla risposta:

Cerca di rendere tutti i tuoi attributi e i relativi accessor il più privati ​​possibile o di non esporli affatto ai clienti. È possibile avere i valori dell'argomento nel costruttore, ovviamente, ma riducendo l'ambito non si consente alla classe client di passare un valore non valido. Se è necessario modificare i valori, è sempre possibile creare un nuovo object. Si controllano i valori solo nel costruttore una volta e nel resto dei metodi si può essere quasi certi che i valori non siano nulli.

Naturalmente, l'esperienza è il modo migliore per comprendere e applicare questo suggerimento.

Byte!

18
OscarRyz

Probabilmente la migliore alternativa per Java 8 o più recente è usare la Optional class. 

Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);

Ciò è particolarmente utile per lunghe catene di valori nulli possibili. Esempio:

Optional<Integer> i = Optional.ofNullable(wsObject.getFoo())
    .map(f -> f.getBar())
    .map(b -> b.getBaz())
    .map(b -> b.getInt());

Esempio su come generare un'eccezione su null:

Optional optionalCarNull = Optional.ofNullable(someNull);
optionalCarNull.orElseThrow(IllegalStateException::new);

Java 7 ha introdotto il metodo Objects.requireNonNull che può essere utile quando qualcosa deve essere controllato per la non nullità. Esempio:

String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();
15
Raghu K Nair

Ignoro molto le risposte che suggeriscono di utilizzare gli oggetti null in ogni situazione. Questo schema potrebbe rompere il contratto e seppellire i problemi sempre più in profondità invece di risolverli, senza menzionare quello usato impropriamente creerà un'altra pila di codice boilerplate che richiederà una manutenzione futura.

In realtà se qualcosa restituito da un metodo può essere nullo e il codice chiamante deve prendere una decisione in merito, dovrebbe esserci una chiamata precedente che garantisca lo stato. 

Inoltre, tieni presente che quel modello di oggetto nullo sarà affamato di memoria se usato senza cura. Per questo - l'istanza di NullObject deve essere condivisa tra i proprietari e non essere un'istanza unigue per ognuno di questi.

Inoltre non consiglierei di usare questo modello in cui il tipo deve essere una rappresentazione di tipo primitiva - come entità matematiche, che non sono scalari: vettori, matrici, numeri complessi e oggetti POD (Plain Old Data), che sono destinati a contenere lo stato sotto forma di tipi integrati di Java. In quest'ultimo caso finirai per chiamare metodi getter con risultati arbitrari. Ad esempio, cosa dovrebbe restituire un metodo NullPerson.getName ()? 

Vale la pena considerare questi casi per evitare risultati assurdi.

14
spectre

Posso rispondere più in generale!

Noi solitamente affrontiamo questo problema quando i metodi ottengono i parametri nel modo in cui non ci aspettavamo (la chiamata al metodo errato è colpa del programmatore). Ad esempio: ci si aspetta di ottenere un oggetto, invece si ottiene un valore nullo. Ti aspetti di ottenere una stringa con almeno un carattere, invece ottieni una stringa vuota ...

Quindi non c'è differenza tra:

if(object == null){
   //you called my method badly!

}

o

if(str.length() == 0){
   //you called my method badly again!
}

Entrambi vogliono assicurarsi che abbiamo ricevuto parametri validi, prima di fare qualsiasi altra funzione.

Come accennato in altre risposte, per evitare problemi sopra puoi seguire il modello Design by contract. Si prega di consultare http://en.wikipedia.org/wiki/Design_by_contract

Per implementare questo modello in Java, è possibile utilizzare le annotazioni Java di base come javax.annotation.NotNull o utilizzare librerie più sofisticate come Hibernate Validator.

Solo un esempio:

getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)

Ora puoi sviluppare in modo sicuro la funzione principale del tuo metodo senza dover controllare i parametri di input, proteggendo i tuoi metodi da parametri imprevisti.

Puoi fare un ulteriore passo avanti e assicurarti che nella tua applicazione possano essere creati solo pojos validi. (campione dal sito di validazione di Hibernate)

public class Car {

   @NotNull
   private String manufacturer;

   @NotNull
   @Size(min = 2, max = 14)
   private String licensePlate;

   @Min(2)
   private int seatCount;

   // ...
}
14
Alireza Fattahi
  1. Mai inizializzare le variabili su null.
  2. Se (1) non è possibile, inizializza tutte le raccolte e gli array per svuotare le raccolte/le matrici.

Fai questo nel tuo codice e puoi evitare! = Assegni nulli.

La maggior parte delle volte i controlli nulli sembrano proteggere i loop su raccolte o array, quindi basta inizializzarli, non è necessario alcun controllo Null.

// Bad
ArrayList<String> lemmings;
String[] names;

void checkLemmings() {
    if (lemmings != null) for(lemming: lemmings) {
        // do something
    }
}



// Good
ArrayList<String> lemmings = new ArrayList<String>();
String[] names = {};

void checkLemmings() {
    for(lemming: lemmings) {
        // do something
    }
}

C'è un piccolo sovraccarico in questo, ma ne vale la pena per un codice più pulito e meno NullPointerExceptions.

13
Stuart Axon

Questo è l'errore più comune si è verificato per la maggior parte degli sviluppatori.

Abbiamo diversi modi per gestirlo.

Approccio 1:

org.Apache.commons.lang.Validate //using Apache framework

notNull (oggetto Object, messaggio String) 

Approccio 2:

if(someObject!=null){ // simply checking against null
}

Approccio 3:

@isNull @Nullable  // using annotation based validation

Approccio 4:

// by writing static method and calling it across whereever we needed to check the validation

static <T> T isNull(someObject e){  
   if(e == null){
      throw new NullPointerException();
   }
   return e;
}
13
public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}
10
tltester

Java 8 ha introdotto una nuova classe Facoltativo nel pacchetto Java.util.

Vantaggi di Java 8 Opzionale:

1.) Non sono richiesti controlli nulli.
2.) Non più NullPointerException in fase di esecuzione.
3.) Possiamo sviluppare API pulite e ordinate. 

Facoltativo - Un oggetto contenitore che può contenere o meno un valore non nullo. Se è presente un valore, isPresent () restituirà true e get () restituirà il valore.

Per ulteriori dettagli, consultare qui i documenti Oracle: - https://docs.Oracle.com/javase/8/docs/api/Java/util/Optional.html

9
NeeruSingh

Puoi usare FindBugs . Hanno anche un Eclipse plugin) che ti aiuta a trovare duplicati controlli nulli (tra le altre cose), ma tieni presente che a volte dovresti optare per la programmazione difensiva. Vi sono anche Contratti per Java che potrebbero essere utili.

8
Leen Toelen

Seguo le linee guida seguenti per evitare i controlli nulli.

  1. Evita il più possibilelazy initializationdelle variabili membro. Inizializza le variabili nella dichiarazione stessa. Questo gestirà NullPointerExceptions. 

  2. Decidere sumutabilitydelle variabili membro all'inizio del ciclo. Usa costrutti di linguaggio come la parola chiave final in modo efficace. 

  3. Se sai che i miglioramenti per il metodo non verranno modificati, dichiarali come final.

  4. Limita il più possibile lamutazionedei dati. Alcune variabili possono essere create in un costruttore e non possono mai essere modificate.Rimuove i metodi di setter pubblico a meno che non siano realmente richiesti.

    Per esempio. Supponiamo che una classe nella tua applicazione (A.Java) stia mantenendo una collezione come HashMap. Non fornire public metodo getter in A.Java e consentire a B.Java di aggiungere direttamente un elemento in Map. Fornisci invece un'API in A.Java, che aggiunge un elemento alla raccolta. 

    // Avoid
    a.getMap().put(key,value)
    
    //recommended
    
    public void addElement(Object key, Object value){
           // Have null checks for both key and value here : single place
           map.put(key,value);
    }
    
  5. E infine, usa try{} catch{} finally{} blocchi nei posti giusti in modo efficace.

7
Ravindra babu

Poiché Java 7 esiste la classe Java.util.Objects.

Ma poiché Java 8, puoi usare i metodi Objects.isNull(var) e Objects.nonNull(var) della classe Objects per eseguire il controllo del puntatore nullo.

Per esempio,

String var1 = null;
Date var2 = null;
Long var3 = null;

if(Objects.isNull(var1) && Objects.isNull(var2) && Objects.isNull(var3))
    System.out.println("All Null");
else if (Objects.nonNull(var1) && Objects.nonNull(var2) && Objects.nonNull(var3))
    System.out.println("All Not Null");
7
Philip John

Tutto sommato per evitare dichiarazioni

if (object != null) {
    ....
}
  1. da Java 7 puoi usare i metodi Objects:

    Objects.isNull (oggetto)

    Objects.nonNull (oggetto)

    Objects.requireNonNull (oggetto)

    Objects.equals (oggetto1, oggetto2)

  2. da Java 8 puoi usare la classe opzionale ( quando usare

object.ifPresent(obj -> ...); Java 8

object.ifPresentOrElse(obj -> ..., () -> ...); Java 9

  1. fare affidamento sul contratto di metodo ( JSR 305 ) e utilizzare Trova bug . Contrassegna il tuo codice con annotazioni @javax.annotation.Nullable e @javax.annotation.Nonnnul. Sono disponibili anche le precondizioni.

    Preconditions.checkNotNull (oggetto);

  2. In casi speciali (ad esempio per stringhe e raccolte) è possibile utilizzare i metodi di utilità Apache-commons (o Google guava):

public static boolean isEmpty (CharSequence cs) // Apache CollectionUtils

public static boolean isEmpty (Collection coll) // Apache StringUtils

public static boolean isEmpty (Mappa) // Apache MapUtils

public static boolean isNullOrEmpty (@Nullable String string) // Guava Strings

  1. Quando è necessario assegnare un valore predefinito quando si utilizza null Apache commons lang

public static Oggetto defaultIfNull (oggetto Object, Object defaultValue)

6
fbokovikov

Un'altra alternativa:

La seguente semplice funzione aiuta a nascondere il controllo null (non so perché, ma non l'ho trovato come parte della stessa libreria common ):

public static <T> boolean isNull(T argument) {
    return (argument == null);
}

Adesso potresti scrivere

if (!isNull(someobject)) {
    someobject.doCalc();
}

che è IMO un modo migliore per esprimere != null.

6
jeha

Ovunque passi un array o un vettore, inizializza questi a quelli vuoti, invece di null. - In questo modo puoi evitare un sacco di controllo per null e tutto è buono :)

public class NonNullThing {

   Vector vectorField = new Vector();

   int[] arrayField = new int[0];

   public NonNullThing() {

      // etc

   }

}
5
Stuart Axon

Puoi evitare molto di più evitando NullPointerException seguendo solo la maggior parte delle altre risposte alla domanda, voglio solo aggiungere alcuni altri modi che sono stati introdotti in Java 9 per gestire questo scenario con garbo e mostrare anche alcuni dei anche i più grandi possono essere usati riducendo così i tuoi sforzi.

  1. public static boolean isNull(Object obj)

    Restituisce true se il riferimento fornito è nullo altrimenti restituisce False.

    Dal momento che Java 1.8

  2. public static boolean nonNull(Object obj)

    Restituisce true se il riferimento fornito non è nullo altrimenti restituisce False.

    Dal momento che Java 1.8

  3. public static <T> T requireNonNullElse​(T obj, T defaultObj)

    Restituisce il primo argomento se non è nullo e restituisce altrimenti Il secondo argomento non nullo.

    Da quando Java 9

  4. public static <T> T requireNonNullElseGet​(T obj, Supplier<? extends T> supplier)

    Restituisce il primo argomento se non è nullo e restituisce in altro modo il valore non nullo di supplier.get ().

    Da quando Java 9

  5. public static <T> T requireNonNull​(T obj, Supplier<String> messageSupplier)

    Verifica che il riferimento all'oggetto specificato non sia null e genera una NullPointerException personalizzata altrimenti.

    Dal momento che Java 1.8

Ulteriori dettagli sulle funzioni sopra possono essere trovati qui .

5
Mukesh A

Trovo che le precondizioni di Guava siano molto utili in questo caso. Non mi piace lasciare null a null pointer exception poiché l'unico modo per capire un NPE è individuando il numero di riga. I numeri di riga nella versione di produzione e nella versione di sviluppo possono essere diversi.

Utilizzando le precondizioni Guava, posso controllare i parametri nulli e definire un messaggio di eccezione significativo in una riga.

Per esempio,

Preconditions.checkNotNull(paramVal, "Method foo received null paramVal");
5
Gal Morad

È possibile utilizzare un intercettore prima della chiamata al metodo. Questo è ciò che programmazione orientata all'aspetto si concentra su.

Supponiamo che M1 (Object test) sia un metodo e M2 sia un metodo in cui applichiamo un aspetto prima di una chiamata al metodo, M2(Object test2). Se test2 != null chiama quindi M1, altrimenti fai un'altra cosa. Funziona con tutti i metodi con cui desideri applicare un aspetto. Se si desidera applicare un aspetto per un campo di istanza e un costruttore, è possibile utilizzare AspettoJ . Spring può anche essere la scelta migliore per un aspetto del metodo.

4

In Java 8 è possibile utilizzare tipo T per la variabile locale/campo/metodo-argomento/metodo-ritorno-tipo se non ha mai assegnato null (e non verificare null) o digitare Optional<T> se può essere null. Quindi utilizzare il metodo map per elaborare T -> e il metodo flatMap per l'elaborazione di T -> Optional<R>:

class SomeService {
    @Inject
    private CompanyDao companyDao;

    // return Optional<String>
    public Optional<String> selectCeoCityByCompanyId0(int companyId) {
        return companyDao.selectById(companyId)
                .map(Company::getCeo)
                .flatMap(Person::getHomeAddress)
                .flatMap(Address::getCity);
    }

    // return String + default value
    public String selectCeoCityByCompanyId1(int companyId) {
        return companyDao.selectById(companyId)
                .map(Company::getCeo)
                .flatMap(Person::getHomeAddress)
                .flatMap(Address::getCity)
                .orElse("UNKNOWN");
    }

    // return String + exception
    public String selectCeoCityByCompanyId2(int companyId) throws NoSuchElementException {
        return companyDao.selectById(companyId)
                .map(Company::getCeo)
                .flatMap(Person::getHomeAddress)
                .flatMap(Address::getCity)
                .orElseThrow(NoSuchElementException::new);
    }
}

interface CompanyDao {
    // real situation: no company for such id -> use Optional<Company> 
    Optional<Company> selectById(int id);
}

class Company {
    // company always has ceo -> use Person 
    Person ceo;
    public Person getCeo() {return ceo;}
}

class Person {
    // person always has name -> use String
    String firstName;
    // person can be without address -> use Optional<Address>
    Optional<Address> homeAddress = Optional.empty();

    public String getFirstName() {return firstName;}   
    public Optional<Address> getHomeAddress() {return homeAddress;}
}

class Address {
    //  address always contains country -> use String
    String country;
    //  city field is optional -> use Optional<String>
    Optional<String> city = Optional.empty();

    String getCountry() {return country;}    
    Optional<String> getCity() {return city;}
}
4
Ivan Golovach

Se stai usando Java8 o successivo, vai per isNull(yourObject) da Java.util.Objects.

Esempio:-

String myObject = null;

Objects.isNull(myObject); //will return true
3
Jobin Joseph

Java 8 ora ha una classe opzionale che considera l'oggetto in considerazione e se un valore è presente, isPresent () restituirà true e get () restituirà il valore.

http://www.Oracle.com/technetwork/articles/Java/java8-optional-2175753.html

3
sidgate

È inoltre possibile utilizzare Checker Framework (con JDK 7 e versioni successive) per verificare in modo statico i valori nulli. Questo potrebbe risolvere molti problemi, ma richiede l'esecuzione di uno strumento aggiuntivo che attualmente funziona solo con OpenJDK AFAIK. https://checkerframework.org/

3
Jochen

OK, ora questo è stato tecnicamente risolto un milione di volte, ma devo dire questo perché questa è una discussione senza fine con i programmatori Java.

Scusa ma non sono d'accordo quasi tutto sopra. Il motivo per cui dobbiamo testare null in Java è perché i programmatori Java non sanno come gestire la memoria. 

Dico questo perché ho una lunga esperienza di programmazione in C++ e non lo facciamo. In altre parole, non è necessario. E nota che, in Java, se colpisci un puntatore pendente, ottieni un'eccezione normale; in C++ questa eccezione normalmente non viene catturata e termina il programma.

Non voglio farlo? Quindi segui alcune semplici regole ala C/C++.

Non istanziare le cose così facilmente, pensa che ogni "nuovo" può farti venire in molti guai e SEGUIRE queste semplici regole.

Una classe deve accedere alla memoria solo in 3 modi ->

  1. Può "HAVE" membri della classe e seguiranno queste regole:

    1. TUTTI i membri "HAS" sono creati "nuovi" nel costruttore.
    2. Chiudere/de allocare in distruttore o equivalente funzione close () In Java per quella stessa classe e in NESSUN altro.

Ciò significa che è necessario avere in mente (proprio come fa Java) chi è il proprietario o il genitore di ciascuna risorsa e rispettare tale proprietà. Un oggetto viene eliminato solo dalla classe che lo ha creato. Inoltre ->

  1. Alcuni membri saranno "USATI" ma non proprietari o "HANNO". Questo è "PROPRIO" in un'altra classe e passato come argomenti al costruttore. Poiché questi sono di proprietà di un'altra classe, non lo elimineremo MAI o lo chiuderemo, solo il genitore può.

  2. Un metodo in una classe può anche istanziare oggetti locali per uso interno che non passeranno MAI lato della classe, o avrebbero dovuto essere normali oggetti "ha".

Infine, affinché tutto funzioni, è necessario disporre di un design disciplinato con classi gerarchiche e senza cicli.

Sotto questa progettazione, E seguendo le regole precedenti, non c'è modo che una classe figlia in una struttura gerarchica possa mai accedere a un puntatore che è stato distrutto, perché ciò significa che un genitore è stato distrutto prima di un bambino, che il disegno aciclico gerarchico non Permettilo.

Infine, ricorda anche quando avvii il tuo sistema dovresti costruire dall'alto verso il basso della gerarchia e distruggere dal basso verso l'alto. Non avrai mai un puntatore nullo da nessuna parte, o qualcuno sta violando le regole.

2
Alex Vaz

Il modo per evitare inutili null-checks è semplice:

You need to know which variables can be null, and which cannot, and you need to be confident about which category a given variable fall into.

Ma, sebbene possa essere dichiarato abbastanza semplicemente, il suo raggiungimento è più difficile. La chiave sta nella parte confident, perché come si può essere sicuri che una variabile non possa essere nulla?

Non ci sono risposte rapide e facili a questo, ma ecco alcuni suggerimenti:

  1. Codice pulito La cosa più importante per essere in grado di ragionare sul comportamento di un pezzo di codice è che è scritto in una materia che è facile da capire. Assegna un nome alle tue variabili in base a ciò che rappresentano, dai il nome ai tuoi metodi dopo quello che fanno, applica Single responsibility principle (S in SOLID: http://en.wikipedia.org/wiki/SOLID_(object-oriented_design) , it significa che ogni pezzo di codice dovrebbe avere una singola responsabilità, e fare questo e nient'altro). Una volta che il codice è pulito, è molto più facile ragionarlo, anche su più livelli/livelli di codice. Con il codice disordinato, cercando di capire che metodo può farti dimenticare perché stai leggendo il metodo in primo luogo. (Suggerimento: leggere "Clean Code" di Robert C. Martin)

  2. Evita di restituire valori null. Se un valore null manterrebbe il corretto funzionamento del tuo programma, lancia invece un exception (assicurati di aggiungere la corretta gestione degli errori). I casi in cui restituire un valore null potrebbe essere accettabile è ad esempio il tentativo di recuperare un oggetto dal database. In questi casi, scrivi il codice che gestisce i valori null e fai una nota dietro l'orecchio che qui abbiamo qualcosa che potrebbe restituire null. Gestire i valori null restituiti il ​​più vicino possibile al chiamante del metodo restituendo null il più possibile (non limitarsi a restituire ciecamente alla catena di chiamata).

  3. Mai MAI passare i valori null espliciti come parametri (almeno non attraverso le classi). Se sei sempre in una posizione in cui il passaggio di un parametro null- è l'unica opzione, la creazione di un nuovo metodo che non ha questo parametro è la strada da percorrere. 

  4. Conferma il tuo contributo! Identifica i "punti di accesso" alla tua applicazione. Possono tutto dai servizi web, ai servizi REST, alle classi EJB remote, ai controller, ecc. Per ciascun metodo in questi punti di accesso, chiedetevi: "Questo metodo verrà eseguito correttamente se questo parametro è nullo?" Se la risposta è no, aggiungi Validate.notNull(someParam, "Can't function when someParam is null!");. Ciò genererà un IllegalArgumentException se il parametro richiesto è mancante. La cosa buona di questo tipo di convalida nei punti di ingresso, è che puoi facilmente assumere nel codice che viene eseguito dal punto di ingresso, che questa variabile non sarà mai nullo! Inoltre, se fallisce, essendo al punto di ingresso, il debug è reso molto più facile di quanto sarebbe se avessi un NullPointerException in profondità nel tuo codice, poiché un errore come questo può significare solo una cosa: il client non ti mando tutte le informazioni richieste Nella maggior parte dei casi si desidera convalidare tutti i parametri di input, se ci si trova in una posizione in cui è necessario consentire molti valori null-, potrebbe essere un segno di un'interfaccia mal progettata, che necessita di refactoring/aggiunte per soddisfare le esigenze dei clienti.

  5. Quando si lavora con Collections, restituisce uno vuoto anziché null! 

  6. Quando si lavora con un database, utilizzare not null- constraints. In questo modo, saprai che un valore letto dal database non può essere nullo e non dovrai controllarlo.

  7. Costruisci il tuo codice e seguilo. Ciò consente di formulare ipotesi sul comportamento del codice, ad esempio se tutti gli input per l'applicazione vengono convalidati, quindi è possibile presumere che questi valori non saranno mai nulli. 

  8. Se non lo stai già facendo, scrivi test automatici del tuo codice. Scrivendo dei test, ragionerai sul tuo codice, e diventerai anche più sicuro di fare ciò che dovrebbe. Inoltre, i test automatici ti proteggono dagli errori durante il refactoring, facendoti sapere immediatamente che questo pezzo di codice non sta facendo quello che era solito fare.

Devi comunque eseguire il controllo nullo, ma può essere ridotto al minimo (cioè la situazione in cui know potresti ottenere un valore nullo, invece che ovunque, per sicurezza.) Quando arriva per i controlli null, in realtà preferisco usare l'operatore ternario (ma usare con cautela, quando inizi a nidificarli diventano davvero disordinati).

public String nullSafeToString(final Object o) {
    return o != null ? o.toString() : "null";
}
2
Tobb

Prima di tutto, non possiamo davvero rimuovere tutte le condizioni nulle. Possiamo ridurli usando le annotazioni @NotNull e @Nullable (come già detto). Ma questo deve essere supportato da qualche quadro. Questo è dove OVal può aiutare.

L'idea di base è oggetto/parametri/costruttore dovrebbe sempre soddisfare le condizioni preliminari. Puoi avere un sacco di precondizioni come Nullable, NotNull e OVal farebbe in modo che un oggetto si trovi in ​​uno stato coerente quando viene invocato.

Immagino che OVal utilizzi internamente AspectJ per convalidare le precondizioni.

@Guarded
public class BusinessObject
{
  public BusinessObject(@NotNull String name)
  {
    this.name = name;
  }

  ...
}

Per esempio,

// Throws a ConstraintsViolatedException because parameter name is null
BusinessObject bo = new BusinessObject(null);
2
Vinay Lodha

Abbiamo utilizzato le librerie Apache (Apache Commons) per questo problema.

ObjectUtils.equals(object, null)

o

CollectionUtils.isEmpty(myCollection);

StringUtils.isEmpty("string");

Mi piace la precedente risposta prima, come pratica, di fornire valori di default iniziali o set vuoti per le collezioni per ridurre al minimo la necessità.

Questi possono essere semplici usi che ti impediscono di avere NullPointerException o utilizzare una raccolta vuota. Questo non risponde alla domanda su cosa fare con l'oggetto nullo, ma fornisce alcuni controlli per le convalide di base dell'oggetto o dell'insieme. 

Spero che questo ti aiuti. 

1
iowatiger08

Preferisco questo

public void simpleFunc(SomeObject someObject){
    someObject = someObject != null ? someObject : new SomeObject(null);
    someObject.doSomething();
}

Naturalmente nel mio esempio SomeObject gestisce con garbo un parametro null. Ad esempio, registrando tale evento e non facendo altro.

1
drzymala

Con Java 8, potresti passare un fornitore a un metodo di supporto come di seguito,

if(CommonUtil.resolve(()-> a.b().c()).isPresent()) {

}

Sopra sostituisce il codice della piastra della caldaia come sotto,

if(a!=null && a.b()!=null && a.b().c()!=null) {

}

//CommonUtil.Java

 public static <T> Optional<T> resolve(Supplier<T> resolver) {
        try {
            T result = resolver.get();
            return Optional.ofNullable(result);
        } catch (NullPointerException var2) {
            return Optional.empty();
        }
    }
1
nilesh

È possibile definire metodi utili che gestiscono i controlli nulli nidificati in modo quasi carino con Java 8 lambda.

void example() {
    Entry entry = new Entry();
    // This is the same as H-MANs solution 
    Person person = getNullsafe(entry, e -> e.getPerson());    
    // Get object in several steps
    String givenName = getNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.getGivenName());
    // Call void methods
    doNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.nameIt());        
}

/** Return result of call to f1 with o1 if it is non-null, otherwise return null. */
public static <R, T1> R getNullsafe(T1 o1, Function<T1, R> f1) {
    if (o1 != null) return f1.apply(o1);
    return null; 
}

public static <R, T0, T1> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, R> f2) {
    return getNullsafe(getNullsafe(o0, f1), f2);
}

public static <R, T0, T1, T2> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Function<T2, R> f3) {
    return getNullsafe(getNullsafe(o0, f1, f2), f3);
}


/** Call consumer f1 with o1 if it is non-null, otherwise do nothing. */
public static <T1> void doNullsafe(T1 o1, Consumer<T1> f1) {
    if (o1 != null) f1.accept(o1);
}

public static <T0, T1> void doNullsafe(T0 o0, Function<T0, T1> f1, Consumer<T1> f2) {
    doNullsafe(getNullsafe(o0, f1), f2);
}

public static <T0, T1, T2> void doNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Consumer<T2> f3) {
    doNullsafe(getNullsafe(o0, f1, f2), f3);
}


class Entry {
    Person getPerson() { return null; }
}

class Person {
    Name getName() { return null; }
}

class Name {
    void nameIt() {}
    String getGivenName() { return null; }
}

(Questa risposta è stata prima pubblicata qui .)

1
Lii

Kotlin con sicurezza nulla è un'alternativa elegante, ma significa un cambiamento più grande.

0
Francis

Per le classi di utilità, è possibile verificare che i parametri non siano nulli.

In tutti gli altri casi, potrebbe non essere necessario. Usa il più possibile l'incapsulamento, riducendo così i posti che ti senti tentato di controllare per nulla.

0
daniel

Puoi abbinare la tua classe a un test unitario usando un framework come JUnit . In questo modo il tuo codice sarà pulito (senza controlli inutili) e sarai sicuro che le tue istanze non saranno nulle.

Questa è una buona ragione (di molti) per utilizzare il Test unità.

0
Mehdi

Il modello di oggetto nullo può essere utilizzato come soluzione per questo problema. Per questo, la classe di someObject dovrebbe essere modificata. 

public abstract class SomeObject {
   public abstract boolean isNil();
}

public class NullObject extends SomeObject {
   @Override
   public boolean isNil() {
      return true;
   }
}
public class RealObject extends SomeObject {
   @Override
   public boolean isNil() {
      return false;
   }
}

Ora è il momento di controllare,

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

Possiamo usare,

if (!someObject.isNil()) {
   someobject.doCalc();
}

Riferimento: https://www.tutorialspoint.com/design_pattern/null_object_pattern.htm

0
Vidura Mudalige

Un'opzione che hai

  • Usa checker framework 's @RequiresNonNull sui metodi. per esempio si ottiene questo se si chiama un metodo annotato come tale, con un argomento nullo. Fallirà durante la compilazione, anche prima dell'esecuzione del codice! poiché in fase di esecuzione sarà NullPointerException

    @RequiresNonNull(value = { "#1" })
    static void check( Boolean x) {
        if (x) System.out.println("true");
        else System.out.println("false");
    }
    
    public static void main(String[] args) {
    
    
        check(null);
    
    }
    

prende 

[ERROR] found   : null
[ERROR] required: @Initialized @NonNull Boolean
[ERROR] -> [Help 1]

Esistono altri metodi come Use Java 8's Optional, Guava Annotations, Pattern Null Object ecc. Non importa se ottieni il tuo obiettivo di evitare! = Null

0
Binod Pant

Un'altra alternativa al controllo! = Null è (se non riesci a liberartene dal punto di vista del design):

Optional.ofNullable(someobject).ifPresent(someobject -> someobject.doCalc());

o

Optional.ofNullable(someobject).ifPresent(SomeClass::doCalc);

Con SomeClass è di tipo someobject.

Tuttavia, non è possibile ottenere un valore di ritorno da doCalc (), quindi è utile solo per i metodi void.

0
Sebastian Brandt

L'approccio funzionale può aiutare a completare i controlli null ripetitivi ed eseguire codice anonimo come nell'esempio seguente.

    BiConsumer<Object, Consumer<Object>> consumeIfPresent  = (s,f) ->{
        if(s!=null) {
            f.accept(s);
        }
    };

    consumeIfPresent.accept(null, (s)-> System.out.println(s) );
    consumeIfPresent.accept("test", (s)-> System.out.println(s));

    BiFunction<Object, Function<Object,Object>,Object> executeIfPresent  = (a,b) ->{
        if(a!=null) {
            return b.apply(a);
        }
        return null;
    };
    executeIfPresent.apply(null, (s)-> {System.out.println(s);return s;} );
    executeIfPresent.apply("test", (s)-> {System.out.println(s);return s;} );
0
Ramprabhu