Controllo nullo durante la navigazione delle gerarchie degli oggetti

3

Ho dovuto implementare del codice che ha attraversato una piccola gerarchia di oggetti per recuperare un valore e visualizzarlo in un oggetto TextView (questo è Android / Java). Ho dovuto fare questo 6 volte per popolare 6 TextViews per vari valori nella gerarchia degli oggetti.

La mia implementazione era Implementazione B . Ma dopo la revisione, il mio collega non era d'accordo e certo che L'implementazione A era la strada da percorrere. Credo che la mia versione non sia solo più pulita, ma anche meno incline agli errori in quanto potrei facilmente perdere qualcosa come sviluppatore.

Potresti fornirmi la tua opinione, con vantaggi e svantaggi per entrambe queste implementazioni?

Implementazione A:

if (house != null && house.getLounge() != null && house.getLounge().getLetter() != null)
{
    String myValue = house.getLounge().getLetter();
    textView.setText(myValue);
}
else
{
    // Do nothing, or maybe make textView hidden.
}

Implementazione B:

try
{
    String myValue = house.getLounge().getLetter();
    textView.setText(myValue);
}
catch (NullPointerException e)
{
    // Do nothing, or maybe make textView hidden.
}
    
posta Eurig Jones 05.09.2014 - 20:43
fonte

5 risposte

4

Dato lo scenario che hai dato, vorrei andare con l'implementazione A.

In entrambe le implementazioni, il caso in cui house è null o house.getLounge() o house.getLounge().getLetter() return null è gestito.

Un problema con l'implementazione B è che tratta un NullPointerException che potrebbe accadere in uno dei metodi chiamati, che è un'occorrenza anormale , come qualcosa di normale. Come hanno sottolineato i commentatori, "ingoia" le eccezioni. Non importa un attimo se si può dimostrare che a partire da oggi , i metodi chiamati non aumenteranno mai NullPointerException . Le modifiche al codice, gli errori vengono introdotti, ciò che pensavamo fosse il caso non è il caso, ecc.

L'implementazione B oscura anche la logica. Quando guardo all'implementazione A, è chiaro che lo sviluppatore si è guardato dal fatto che house è null , ecc. So quale serie di condizioni causerà il ramo "Non fare nulla ...". Nell'implementazione B qual è l'insieme di condizioni che genererà un NullPointerException ? getLounge a volte genera un NullPointerException ? Non lo so senza guardare altrove.

    
risposta data 05.09.2014 - 21:31
fonte
3

Vantaggio di A :

Metti in chiaro, sotto quale condition vuoi fare qualcosa:

house != null && house.getLounge() != null && house.getLounge().getLetter() != null

Questa è la condizione. E vuoi get un valore e set un altro valore.

String myValue = house.getLounge().getLetter();
textView.setText(myValue);

Quindi, in termini di comunicazione di intent , questo è chiaro . Btw. la seconda condizione non è necessaria. Se una stringa è nullo, non deve essere visualizzato nulla.

Perché B è sbagliato:

Con try-catch comunichi: »Voglio fare x , che è in qualche modo pericoloso« - che in realtà non lo è. Il tuo intento non è chiaro come in A . E - nei miei occhi peggio - offuschi una possibilità di errore:

String myValue = house.getLounge().getLetter();
textView.setText(myValue);

A NullPointerException viene lanciato a) quando getLounge() restituisce null eb) quando textView è nullo. Poiché stai prendendo one NPE, non sai quale. Ovviamente in un caso così semplice dovrebbe essere facile debug , ma in generale questo è modo sbagliato .

Sono favorevole a una terza via:

String myValue = (house.getLounge()!=null)?house.getLounge().getLetter:"";

Naturalmente, c'è Diskussion sull'uso di ternary Operator (alcune lingue hanno abbandonato il concetto), ma in questo caso si adatta meglio: sei in grado di scrivere un oneliner pulito, che fornisce un default .

    
risposta data 06.09.2014 - 10:04
fonte
2

Un'altra opzione

import java.util.Optional;

Optional.fromNullable(house)
               .map(House::getLounge)
               .map(Lounge::getLetter)
               .ifPresent(letter -> textView.setText(letter));

Questo evita l'uso di eccezioni per il controllo del flusso ma evita anche la gestione dettagliata di ogni possibile null. Invece quella logica è parte della logica della mappa opzionale.

    
risposta data 24.06.2018 - 23:20
fonte
0
  1. La versione A è auto-descrittiva. Mira a fare esattamente quello che dice. Guardando la versione B presa fuori dal contesto, non ho idea se la difesa debba essere contro casa essendo null o textField essendo null (perché, forse, è disponibile su alcuni layout e non disponibile su altri layout - Questo non sarebbe strano quando si tratta di risorse Android).

  2. Quando un giorno textField è null a causa di alcuni errori di battitura introdotti nel file di layout (che è piuttosto probabile a lungo termine - findViewById return null se non trova) - la versione A renderà l'errore ovvio e genererà un'eccezione utile. E la versione B nasconderà l'errore, rendendo un errore facile un bug davvero oscuro, ben nascosto.

  3. L'unica cosa da dire per la versione "b" è che è più breve di 53 caratteri. Che è modo a poco per renderlo una buona scelta.

risposta data 05.09.2014 - 22:25
fonte
-2

Sono in ritardo, ma voglio descrivere perché preferisco B.

Idealmente, vuoi solo

String myValue = house.getLounge().getLetter();
textView.setText(myValue);

Ma ci sono così tanti modi in cui questo codice fallirà. NPE è solo una delle possibilità

getLounge() può generare qualche runtime DBException

getLetter() potrebbe restituire una cifra che textView.setText(..) potrebbe non accettare e generare un'eccezione.

Quindi hai scritto un codice che verrebbe eseguito nel caso in cui il codice desiderato fallisse o fallisse.

 // Do nothing, or maybe make textView hidden.

Supponendo che si tratti di un codice di produzione, si desidera garantire la maggior parte delle funzionalità. Non vuoi che un errore delle 2 righe di codice non influisca su nessun'altra cosa. La tua voglia di proteggerlo. La guardia perfetta sarebbe

try
{
    String myValue = house.getLounge().getLetter();
    textView.setText(myValue);
}
catch (Exception e)
{
    // Log the problem
    // Do nothing, or maybe make textView hidden.
}

Proprio perché NPE è la minaccia più comune e visibile, si è protetti solo contro NPE. Se la tua intenzione è di proteggere solo NPE, allora potresti essere meglio fare i controlli nulli. Forse, non dovresti aver colpito questa logica in primo luogo con un controllo nullo appropriato e una cache di house.getLounge() . Ma se la tua intenzione è di non lasciare che un problema di textView non venga propagato, penso che potresti catch Exception .

    
risposta data 23.06.2018 - 07:12
fonte

Leggi altre domande sui tag