back to top

Java: cos’è l’errore NullPointerException e come risolverlo

RuntimeException รจ una superclasse di Java che ricomprende tutte le eccezioni che possono essere lanciate nel corso della normale operativitร  della JVM (Java Virtual Machine).

Nei linguaggi di programmazione orientati agli oggetti, come appunto Java, una superclasse รจ una classe generica che puรฒ essere estesa in sottoclassi, altrimenti dette classi figlie, che hanno il compito di fornire delle versioni specializzate delle classi principali da cui derivano. Nel caso specifico di RuntimeException e delle sue sottoclassi si parla nello specifico di unchecked exceptions, cioรจ di espressioni che non รจ necessario dichiarare in un metodo e che si manifestano automaticamente in fase di runtime quando si verificano determinate condizioni.

Quando si verifica l’errore NullPointerException?

Un caso abbastanza frequente di RuntimeException รจ rappresentato dalla sottoclasse chiamata NullPointerException (o piรน estesamente java.lang.NullPointerException) corrispondente ad unโ€™eccezione che viene lanciata quando unโ€™applicazione tenta di utilizzare la referenza di un oggetto che ha null come valore, proprio per questo motivo parliamo di โ€œeccezione a puntatore nulloโ€.

Variabili, oggetti e puntatori

A livello pratico un oggetto รจ sostanzialmente unโ€™entitร  associata ad una variabile di riferimento, nello stesso tempo รจ possibile affermare che la dichiarazione di questโ€™ultima determina la creazione di un puntatore verso lโ€™oggetto a cui si riferisce. Per comprendere il funzionamento di questo meccanismo รจ possibile proporre lโ€™esempio di una comune variabile di tipo intero a cui successivamente viene associato un valore:

int numero;
numero = 5;

Abbiamo quindi una primitiva, denominata numero, che come anticipato viene dichiarata come un intero a cui Java attribuisce il valore 0 in fase di inizializzazione. Nel momento in cui a numero viene dato un valore specifico (5 nel codice proposto) esso viene archiviato nella porzione di memoria che rappresenta il riferimento di numero.

Ora l’esempio presentato puรฒ essere riproposto nel modo seguente in cui invece di dichiarare una variabile associandola al suo tipo di dato si dichiara direttamente un tipo di riferimento:

Integer numero;
numero = new Integer(5);

Come รจ possibile notare la dichiarazione della variabile numero avviene ugualmente, la differenza rispetto al codice precedente sta invece nel fatto che ad essa non viene attribuito un valore al momento dellโ€™inizializzazione. Questo accade perchรฉ in Java int e Integer non hanno lo stesso significato a livello sintattico, esattamente come accade per char e Character nel caso delle stringhe o a float e Float per i decimali.

int infatti non รจ una classe ma un tipo primitivo che opera come rappresentazione di unโ€™informazione utilizzabile per lโ€™elaborazione di calcoli numerici, informazione che in pratica รจ un valore mutabile a runtime anche dopo la dichiarazione della variabile int.

Integer invece รจ una classe contenente un campo intero, motivo per il quale รจ possibile rappresentarla come un contenitore di un int (classe wrapper). A ciรฒ si aggiunga che il valore di Integer non รจ modificabile durante lโ€™esecuzione di un programma e per potergli attribuire un nuovo valore รจ necessario ricorrere al costrutto new Integer(n) dove โ€œnโ€ รจ un numero intero.

Ritornando al codice, nella prima riga a numero non viene associato un valore ma abbiamo comunque la generazione del puntatore in quanto Integer รจ un tipo di riferimento. Il puntatore perรฒ non dispone di alcuna coordinata per il puntamento e lโ€™unica possibilitร  per Java รจ quella di impostare il valore su null che, notoriamente, a livello semantico non corrisponde esattamente a 0 ma allโ€™assenza di un valore e di conseguenza allโ€™inesistenza di un oggetto.

Per evitare questa eventualitร  viene richiamata la keyword new con cui istanziare un oggetto Integer reindirizzando il puntatore su questโ€™ultimo. Se invece si dichiara una variabile senza creare lโ€™oggetto corrispondente, e si prova ad accedere alle informazioni della variabile stessa, si avrร  una NullPointerException.

A tal proposito, un caso particolare รจ quello dellโ€™autoboxing, la conversione automatica che il compilatore Java opera tra tipi primitivi e gli oggetti delle classi wrapper corrispondenti, come quando ci si trova davanti ad unโ€™espressione di questo genere:

int numero = y

con cui potrebbe essere prodotta una NullPointerException nel caso in cui y sia Integer.

Prevenire unโ€™eccezione a puntatore nullo

La NullPointerException si verifica quindi ogni volta che un programma tenta di utilizzare la referenza di un oggetto che ha null come valore, questo puรฒ accadere anche quando si prova ad invocare un metodo da un oggetto null (comunemente nel momento in cui si usa il โ€œ.โ€ per la chiamata al metodo) oppure nel tentativo di accedere al campo di un oggetto null o di modificarlo.

Il modo migliore per prevenire unโ€™eccezione a puntatore nullo รจ quello di assicurarsi che tutti gli oggetti vengano inizializzati correttamente prima del loro utilizzo, ad esempio prima della richiesta di un metodo o di un campo da un oggetto.

Sulla base di quanto detto osserviamo come il codice seguente non possa fare altro che produrre una NullPointerException:

// Invocazione di un metodo con restituzione di una NullPointerException
import java.io.*;
class RestituisciNullPointerException
{
  public static void main (String[] args)
  {
    // inizializzazione di una variabile con valore null
    String x = null;
    // confronto per uguaglianza tramite equals()
    try
    {
      if (x.equals("Una stringa a caso"))
        System.out.print("Corrispondenza rilevata");
      else
        System.out.print("Corrispondenza mancante");
    }
    // restituzione del messaggio di errore in caso di eccezione
    catch(NullPointerException e)
    {
      System.out.print("Attenzione, rilevata una NullPointerException");
    }
  }
}

Il metodo equals() consente di confrontare lโ€™oggetto su cui viene invocato con un altro oggetto passato sotto forma di parametro, restituendo in output un valore booleano. Nel codice proposto la generazione del messaggio di errore โ€œAttenzione, rilevata una NullPointerExceptionโ€ รจ dovuta al fatto che il metodo equals() viene richiamato a partire da un oggetto null (โ€œx.equals(..)โ€).

// Invocazione di un metodo senza NullPointerException
import java.io.*;
class NonRestituisciNullPointerException
{
  public static void main (String[] args)
  {
    // inizializzazione di una variabile con valore null
    String x = null;
    // verifica sul valore di x
    try
    {
      if ("Una stringa a caso".equals(x))
        System.out.print("Corrispondenza rilevata");
      else
        System.out.print("Corrispondenza mancante");
    }
    // restituzione del messaggio di errore in caso di eccezione
    catch(NullPointerException e)
    {
      System.out.print("Attenzione, rilevata una NullPointerException");
    }
  }
}

Lo scopo del programma mostrato in precedenza รจ quello di effettuare un confronto tra una variabile String e una literal, dove per literal si intende un valore costante che puรฒ essere assegnato ad una variabile. Le literal possono essere String o Enum, cioรจ un tipo enumerabile con cui vincolare il valore di una variabile ad uno specifico set di valori definito preventivamente in sede di programmazione.

Dopo aver compilato il codice, il programma restituisce lโ€™output โ€œCorrispondenza mancanteโ€ in fase di esecuzione perchรฉ questa volta equals() non viene invocato su un oggetto null ma su una literal utilizzata come termine di confronto con la variabile String.

In generale รจ possibile affermare che un’eventuale a NullPointerException dovrebbe essere evitata o gestita in fase di produzione ma puรฒ risultare utile per finalitร  di test. Si pensi per esempio a tutti casi in cui viene passato come input un parametro null ma questโ€™ultimo non รจ un parametro valido per il metodo utilizzato. Se un metodo ha il compito di effettuare una qualche operazione a carico di un oggetto, lโ€™eccezione a puntatore nullo consente di evidenziare errori di programmazione risultando congeniale per il debugging.

Pubblicitร 
Claudio Garau
Claudio Garau
Web developer, programmatore, Database Administrator, Linux Admin, docente e copywriter specializzato in contenuti sulle tecnologie orientate a Web, mobile, Cybersecurity e Digital Marketing per sviluppatori, PA e imprese.

Leggi anche...

Java: cos’è e a cosa serve l’operatore modulo (%)

In Java, l'operatore modulo è rappresentato dal simbolo "%"...

Arrotondare un numero con Java

Quando si lavora con i numeri all'interno di un...

Stringhe in Java

La gestione delle stringhe assume un ruolo fondamentale in...

Leggere un file di testo in java

In java ci sono diverse modalità per leggere dati...

Il costrutto foreach in java

Il foreach è un particolare costrutto (disponibile a partire...

Java split() – dividere una stringa

Grazie al metodo split() della classe String di Java...
Pubblicitร