L'errore più comune in Java
Una NullPointerException (tutti la chiamano NPE) si verifica quando provi a usare un riferimento che non punta a niente, cioè null, come se puntasse a un oggetto vero. In Java le variabili di tipo oggetto contengono un oggetto oppure null, il valore che significa "qui non c'è nessun oggetto". Nel momento in cui chiedi a null di fare qualcosa (chiamare un suo metodo, leggere un suo campo, accedere a un indice) non c'è niente su cui agire, quindi la JVM lancia l'eccezione.
A differenza di try-catch, che hai visto nella pagina precedente per gestire errori previsti, una NPE è quasi sempre un semplice bug. Lo scopo di questa pagina non è catturarle: è capire perché si verificano e scrivere codice che non le produca.
Non c'è nessuna String su cui eseguire length(), quindi il programma si ferma con una NullPointerException.
Leggere il messaggio
Da Java 14 il messaggio ti dice esattamente cosa era null: si chiama Helpful NullPointerException. Leggilo prima di cambiare qualsiasi cosa:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "name" is null
at Main.main(Main.java:4)
Contano due parti. Cannot invoke "String.length()" è l'operazione che è fallita, e because "name" is null indica il colpevole. La riga at Main.main(Main.java:4) è lo stack trace che punta alla riga esatta. Quindi la soluzione non è "metti la riga 4 in un try", ma "scopri perché name è null alla riga 4". Quasi sempre il bug si trova più in alto, dove il valore avrebbe dovuto essere assegnato e non lo è stato.
I modi in cui la provochi
Le NPE nascono da poche operazioni, tutte varianti di "toccare null":
Le ricerche nelle mappe sono una fonte classica: get restituisce null quando la chiave non c'è, e la NPE spesso compare molte righe dopo, quando finalmente usi quel valore. L'unboxing è il caso più subdolo: assegnare un Integer che vale null a un int lancia l'eccezione, perché non c'è nessun numero da copiare.
Proteggersi con un controllo su null
La difesa più semplice è un if che verifica che un riferimento non sia null prima di usarlo:
Quando confronti una variabile con una String costante, metti prima la costante: "yes".equals(answer) invece di answer.equals("yes"). Se answer è null, la prima forma restituisce tranquillamente false, mentre la seconda lancia l'eccezione.
Fallire subito con Objects.requireNonNull
Spargere controlli su null ovunque rende il codice rumoroso. Quando un valore non dovrebbe mai essere null, come l'argomento di un costruttore, validalo al confine con Objects.requireNonNull. Lancia subito l'eccezione, con un messaggio chiaro, nel punto in cui arriva il valore sbagliato invece che in profondità nel codice più tardi:
L'abitudine di "fallire subito" trasforma una NPE vaga a 200 righe di distanza in una segnalazione precisa all'origine. Qui l'eccezione viene catturata solo per mostrare il messaggio: nel codice reale la lasceresti emergere perché il bug venga corretto.
Evitare i null fin dall'inizio
La NPE migliore è quella che non può mai verificarsi perché non c'è nessun null da incontrare. Alcune abitudini aiutano molto:
- Restituisci una collezione o una stringa vuota, mai
null.Collections.emptyList()e""sono sicuri da scorrere in un ciclo e su cui chiamare metodi. - Usa
getOrDefaultsulle mappe, così una ricerca a vuoto produce un valore vero invece dinull. - Inizializza i campi quando li dichiari invece di lasciarli
nullfino a "più tardi".
Quando un valore è davvero facoltativo, come una ricerca che può legittimamente non trovare niente, Java offre Optional, un contenitore che obbliga il chiamante a gestire il caso "assente" invece di restituire in silenzio un null. È il concetto correlato da leggere dopo se vuoi eliminare del tutto questi vuoti dalle tue API.
Riepilogo
Una NullPointerException è Java che ti dice che hai usato un riferimento che contiene null come se contenesse un oggetto. La soluzione raramente è catturarla: devi leggere il messaggio dettagliato, risalire al punto in cui il valore avrebbe dovuto essere impostato e poi garantire che non sia null oppure proteggere il punto in cui lo usi. Affidati a Objects.requireNonNull per fallire subito ai confini, preferisci valori vuoti e getOrDefault a null, e usa Optional quando l'assenza è un risultato reale e previsto. Con questa mentalità, l'errore più comune in Java diventa uno dei più rari nel tuo codice.
Domande frequenti
Cosa causa una NullPointerException in Java?
Succede quando usi un riferimento che punta a null come se puntasse a un oggetto vero: per esempio quando chiami un metodo (name.length()), leggi un campo, accedi a un elemento di un array o fai unboxing di un Integer che vale null. La variabile non contiene alcun oggetto, quindi non c'è niente su cui agire e la JVM lancia una NullPointerException.
Come si risolve una NullPointerException in Java?
Leggi il messaggio: da Java 14 indica esattamente cosa era null (per esempio "Cannot invoke "String.length()" because "name" is null"). Poi scopri perché quella variabile è null: un'inizializzazione mancante, un metodo che ha restituito null o una ricerca in una mappa andata a vuoto. Correggi l'origine in modo che il valore non sia mai null, oppure proteggi l'uso con un controllo su null, con Objects.requireNonNull o con Optional.
È meglio controllare null o catturare una NullPointerException?
Controlla null. Una NullPointerException segnala un bug nella tua logica, non una situazione prevista, quindi va prevenuta invece che catturata. Catturarla nasconde dove si trova il vero problema. Riserva try/catch alle situazioni davvero eccezionali, come gli errori di I/O.