Domanda

Se ricevo una NullPointerException in una chiamata come questa:

someObject.getSomething().getSomethingElse().
    getAnotherThing().getYetAnotherObject().getValue();

Ricevo un testo di eccezione piuttosto inutile come:

Exception in thread "main" java.lang.NullPointerException
at package.SomeClass.someMethod(SomeClass.java:12)

Trovo piuttosto difficile scoprire quale chiamata effettivamente ritorni a null, trovandomi spesso a refactoring il codice in qualcosa del genere:

Foo ret1 = someObject.getSomething();
Bar ret2 = ret1.getSomethingElse();
Baz ret3 = ret2.getAnotherThing();
Bam ret4 = ret3.getYetAnotherOject();
int ret5 = ret4.getValue();

e quindi in attesa di una NullPointerException più descrittiva che mi dica quale linea cercare.

Alcuni di voi potrebbero obiettare che la concatenazione di getter è un cattivo stile e dovrebbe comunque essere evitata, ma la mia domanda è: posso trovare il bug senza cambiare il codice?

Suggerimento: sto usando eclipse e so cos'è un debugger, ma non riesco a capire come applicarlo al problema.

Le mie conclusioni sulle risposte:
Alcune risposte mi hanno detto che non avrei dovuto concatenare i getter uno dopo l'altro, alcune risposte mi mostravano come eseguire il debug del mio codice se avessi rifiutato quel consiglio.

Ho accettato una risposta che mi ha insegnato esattamente quando incatenare i vincitori:

  • Se non riescono a restituire null, concatenali quanto vuoi. Non c'è bisogno di controllare! = Null, non c'è bisogno di preoccuparsi di NullPointerExceptions ( essere avvisato che il concatenamento vialota ancora la legge di demeter, ma posso conviverci )
  • Se possono restituire null, mai e poi mai e poi mai li incatenano ed eseguono un controllo per valori null su ognuno che può restituire null

Questo rende inutili tutti i buoni consigli sull'effettivo debug.

È stato utile?

Soluzione

La risposta dipende da come vedi (il contratto di) i tuoi vincitori. Se possono restituire null , dovresti davvero controllare il valore di ritorno ogni volta. Se il getter non deve restituire null , il getter dovrebbe contenere un segno di spunta e generare un'eccezione ( IllegalStateException ?) Invece di restituire null , che ha promesso di non tornare mai più. Lo stacktrace ti indicherà l'esatto getter. Potresti anche mettere lo stato imprevisto che il tuo getter ha trovato nel messaggio di eccezione.

Altri suggerimenti

NPE è l'eccezione più inutile in Java, punto. Sembra essere sempre pigramente implementato e non dice mai esattamente cosa lo abbia causato, anche se semplice come "la classe x.y.Z è nulla" aiuterebbe molto nel debug di questi casi.

Comunque, l'unico buon modo che ho trovato per trovare il lanciatore NPE in questi casi è il seguente tipo di refactoring:

someObject.getSomething()
          .getSomethingElse()
          .getAnotherThing()
          .getYetAnotherObject()
          .getValue();

Eccolo qui, ora NPE punta alla linea corretta e quindi al metodo corretto che ha lanciato l'NPE reale. Non è una soluzione elegante come vorrei che fosse, ma funziona.

In IntelliJ IDEA è possibile impostare eccezioni> . Tali punti di interruzione si attivano ogni volta che viene generata un'eccezione specifica (è possibile estenderla a un pacchetto o una classe).

In questo modo dovrebbe essere facile trovare la fonte del tuo NPE.

Suppongo che tu possa fare qualcosa di simile in netbeans o eclissi.

EDIT: Qui è una spiegazione su come aggiungere un punto di eccezione in eclipse

Se ti ritrovi a scrivere spesso:

a.getB().getC().getD().getE();

questo è probabilmente un odore di codice e dovrebbe essere evitato. Ad esempio, puoi effettuare il refactoring in a.getE () che chiama b.getE () che chiama c.getE () che chiama d.getE () . (Questo esempio potrebbe non avere senso per il tuo particolare caso d'uso, ma è uno schema per correggere questo odore di codice.)

Vedi anche la Law of Demeter , che dice:

  • Il tuo metodo può chiamare direttamente altri metodi nella sua classe
  • Il tuo metodo può chiamare metodi direttamente sui propri campi (ma non sui campi dei campi)
  • Quando il metodo accetta parametri, il metodo può chiamare direttamente i metodi su tali parametri.
  • Quando il tuo metodo crea oggetti locali, quel metodo può chiamare metodi sugli oggetti locali.

Pertanto, non si dovrebbe avere una catena di messaggi, ad es. a.getB (). Getc (). DoSomething () . A seguito di questa "legge" ha molti più vantaggi oltre a rendere più semplice il debug di NullPointerExceptions.

In genere non concatenare getter come questo dove c'è più di un getter annullabile.

Se corri all'interno del tuo ide, puoi semplicemente impostare un punto di interruzione e usare l'espressione "valuta l'espressione" funzionalità del tuo ide su ogni elemento in successione.

Ma ti gratterai la testa nel momento in cui ricevi questo messaggio di errore dai log del tuo server di produzione. Pertanto, tieni al massimo un elemento annullabile per riga.

Nel frattempo possiamo sognare operatore di navigazione sicura di Groovy

Anche il fallimento anticipato è un'opzione.

In qualsiasi parte del codice in cui è possibile restituire un valore null, prendere in considerazione l'introduzione di un controllo per un valore restituito null.

public Foo getSomething()
{
  Foo result;
  ...
  if (result == null) {
    throw new IllegalStateException("Something is missing");
  }
  return result;
}

Ecco come trovare il bug, usando Eclipse.

Innanzitutto, imposta un punto di interruzione sulla linea:

someObject.getSomething().getSomethingElse().
getAnotherThing().getYetAnotherObject().getValue();

Esegui il programma in modalità debug, consenti al debugger di passare alla sua prospettiva quando la linea viene colpita.

Ora, evidenzia " someObject " e premi CTRL + MAIUSC + I (o fai clic con il pulsante destro del mouse e dì " ispeziona ").

È nullo? Hai trovato il tuo NPE. È non nullo? Quindi evidenziare someObject.getSomething () (inclusa la parentesi) e ispezionarlo. È nullo? Ecc. Continua lungo la catena per capire dove si sta verificando il tuo NPE, senza dover cambiare il tuo codice.

Puoi fare riferimento a questa domanda sull'evitamento! = null .

Fondamentalmente, se null è una risposta valida, devi verificarla. In caso contrario, affermalo (se puoi). Ma qualunque cosa tu faccia, prova a ridurre al minimo i casi in cui null è una risposta valida per questo tra l'altro.

Se devi arrivare al punto in cui dividi la linea o esegui un debug elaborato per individuare il problema, allora questo è generalmente il modo di Dio di dirti che il tuo codice non sta verificando il null abbastanza presto .

Se hai un metodo o un costruttore che accetta un parametro oggetto e l'oggetto / metodo in questione non è in grado di gestire sensibilmente quel parametro essendo nullo, allora controlla e lancia una NullPointerException lì e poi.

Ho visto persone inventare "stile di codifica" regole per provare a risolvere questo problema come " non ti è permesso più di un punto su una linea " ;. Ma questo incoraggia solo la programmazione che individua il bug nel posto sbagliato.

Espressioni concatenate del genere sono una seccatura da debug per NullPointerExceptions (e la maggior parte degli altri problemi che possono verificarsi), quindi ti consiglio di provare ad evitarlo. Probabilmente avete già sentito abbastanza e, come menzionato in un precedente poster, è possibile aggiungere punti di interruzione sull'attuale NullPointerException per vedere dove si è verificato.

In eclipse (e nella maggior parte degli IDE) è anche possibile utilizzare espressioni watch per valutare il codice in esecuzione nel debugger. Fai questo bu selezionando il codice e usa il menu contet per aggiungere un nuovo orologio.

Se hai il controllo del metodo che restituisce null, puoi anche considerare il modello Null Object se null è un valore valido da restituire.

Posiziona ogni getter sulla sua linea ed esegui il debug. Passa sopra (F6) ogni metodo per trovare quale chiamata restituisce null

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