Durante una sua presentazione nel 2009, il famoso computer scientist Tony Hoare ha raccontato come abbia inventato la referenza a null nel 1965 nel linguaggio ALGOL. In questa stessa presentazione, Hoare ha chiamato questa invenzione un “billion-dollar mistake”.

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/

Se programmate in un linguaggio che supporta la referenza a null, vi sarà sicuramente capitato di imbattervi in un null pointer exception. Nel 2022 però molti linguaggi di programmazione hanno introdotto nuovi costrutti per evitare l’uso di null, mentre in altri casi è possibile evitarli impiegando tecniche di programmazione specifiche.

Evitare di inizializzare variabili con null

In generale una variabile dovrebbe vivere il minor tempo possibile, quindi dovrebbe essere dichiarata nello stesso scope in cui viene utilizzata. A volte però succede che l’inizializzazione sia complicata tale per cui può tornare utile separare la dichiarazione impostando la variabile temporaneamente a null.

public String getSummary(Page p) {
    String content= null;

    if (p.hasTitle()) {
        content= p.getTitle();
    } else if (p.getContent()) {
        content = p.getContent();
    } else {
        content = "";
    }

    return summarize(content);
}

A seconda dei dati disponibili in Page, summary assumerà valori differenti. Per evitare di usare null in questo caso abbiamo due opzioni. La prima opzione è quello di usare un default. Nel caso in cui ne il titolo ne il contenuto siano disponibili, andremo a impostare content a stringa vuota. Quindi possiamo direttamente inizializzare content a stringa vuota e rimuovere il branch else.

Il secondo modo di evitare di usare null, consiste nella creazione di un metodo atto alla inizializzazione di content:

public string getContent(Page p) {
    if (p.hasTitle()) {
        return p.getTitle();
    } else if (p.getContent()) {
        return p.getContent();
    } 

    return  "";
}
public String getSummary(Page p) {
    return summarize(getContent);
}

Evitare di ritornare null da un metodo

Prendiamo ad esempio un database. Quando richiediamo una entry associata ad un certo Id, e questo Id è assente, cosa dovrebbe tornare tale metodo?

Possiamo rapportare lo stesso esempio ad una struttura dati. Nella libreria standard di Java, quando in una hashmap chiediamo un dato associato ad un certo id e questo è assente, avremo di ritorno un null pointer. Leggendo la dichiarazione del metodo get, leggiamo:

    public V get(key K)

Quindi di ritorno ci aspettiamo dati di tipo V. Essendo null padre di ogni tipo, è possibile ritornarlo da qualunque metodo. Il problema è che leggendo questa dichiarazione, ci aspetteremo sempre un oggetto di tipo V. Senza leggere la documentazione di questo metodo o il suo codice, non potremmo sapere se get può o non può tornare null.

Direttamente dal mondo della programmazione funzionale, ci arriva un Monad che permette di risolvere il dilemma di cosa tornare in caso non avessimo nulla da tornare, Optional. L’idea è avere una struttura dati simile ad una box che può contenere qualcosa o niente (null). Leggere:

    public Optional<T> get(key K) 

Ci è sufficiente per sapere che get potrebbe tornare null.

Evitare di passare e ricevere parametri null

E’ una buona pratica quella di usare la classe Optional solamente nel caso di valori di ritorno.

Nel caso in cui il nostro metodo possa accettare parametri opzionali, in Java è preferibile creare metodi che accettino quel numero preciso di parametri. Questo non solo permette di evitare di passare null in giro, ma aiuta anche la scrittura del codice in quanto la documentazione del metodo è nella sua signature.

Quando è accettabile usare null

Come già abbiamo detto, in generale null dovrebbe essere evitato. E’ accettabile l’utilizzo di null quando è confinato all’interno della stessa classe, ad esempio per la dichiarazione degli attributi di una classe. In tutti gli altri casi, è possibile rimpiazzare l’uso di null.

Conclusione

Tony Hoare ha chiamato l’invenzione di “null” un errore da 1 miliardo di dollari e non a caso: moltissimi bug sono stati e continuano ad essere riscontrati a causa di null pointer exceptions. Grazie a nuovi costrutti introdotti nel tempo nei linguaggi di programmazione ed alle best practices per l’implementazione del codice, è praticamente sempre possibile evitare l’utilizzo del tipo null.