Java – La parola chiave void e il tipo Void

In Java esiste la parola chiave void (“v” minuscola) e il tipo Void (“V” maiuscola) ovvero la classe java.lang.Void. Sono due cose diverse e con un significato e uso ben differente. In questo articolo spiegherò le differenze e l’utilizzo di questi due elementi.

La parola chiave void

Innanzitutto void è una keyword (“parola chiave”) ed è quindi una parola riservata del linguaggio che non può essere usata per altri scopi, nemmeno come “identificatore” (cioè come nome di variabile, di metodo, di tipo, ecc...). La cosa più importante da chiarire però è che void non è un tipo di dato, quindi non può essere usato per dichiarare una variabile o un parametro. Inoltre void non può essere usato per un cast (mentre invece nei linguaggi C/C++ è una operazione lecita). Quest’ultimo è un aspetto un po’ particolare ma da ricordare.

In Java la parola chiave void in effetti ha principalmente un solo ed unico uso: dichiarare che un metodo non restituisce alcun valore, come nel seguente metodo di esempio.

public static void unMetodo() {
    // .....
}

Nell’esempio riportato sopra, void indica che il metodo non restituisce al chiamante alcun valore e pertanto deve terminare o in maniera “naturale”, cioè alla fine del corpo { } del metodo, oppure in maniera esplicita con un return; che deve essere chiaramente senza un valore.

Il void, come detto all’inizio, non è un tipo di dato ma in un caso particolare può essere trattato come “pseudo” tipo per ottenere il java.lang.Class relativo a void utilizzando il class literal void.class, che denota il riferimento ad un oggetto di tipo Class<Void>. Questa forma d’uso serve quasi esclusivamente in un solo contesto: quando si usa la reflection di Java. Un esempio veramente banale potrebbe essere la ricerca di tutti i metodi dichiarati in una certa classe (es. StringBuffer) che hanno void come tipo di ritorno. Il seguente codice mostra un esempio minimale.

import java.lang.reflect.Method;

public class TrovaMetodiVoid {
    public static void main(String[] args) {
        Method[] metodi = StringBuffer.class.getDeclaredMethods();

        for (Method metodo : metodi) {
            if (metodo.getReturnType() == void.class) {
                System.out.println(metodo);
            }
        }
    }
}

Il tipo java.lang.Void

Il tipo java.lang.Void è una classe presente all’interno del framework della piattaforma Standard Edition di Java ed appartiene quindi alla categoria dei tipi reference.

La classe Void presenta le seguenti caratteristiche:

  • è marcata con il modificatore final (non si può estendere)
  • non ha un costruttore pubblico (non si può istanziare, a meno di ricorrere ai soliti “truschini” con la reflection di Java)
  • non definisce nuovi metodi (a parte quelli “ereditati” da Object)

Date queste caratteristiche, Void potrebbe sembrare una classe inutile, perché in effetti con una variabile di questo tipo l’unica cosa che si può lecitamente assegnare è un null letterale. In realtà, Void è utile in un contesto molto specifico: nelle parametrizzazioni dei tipi generici (i generics introdotti in Java 5).

Bisogna infatti ricordare che per le parametrizzazioni dei tipi generici si possono utilizzare solamente i tipi reference, quindi ad esempio si può fare un List<Integer> ma non si può fare List<int>. Ci sono talvolta delle situazioni in cui un parametro di tipo non è importante o in generale si può ignorare perché non serve, specialmente quando viene usato come tipo di ritorno di un metodo. Se una classe/interfaccia è generica, dovrebbe sempre essere usata parametrizzata al fine di evitare warning e se il parametro di tipo è ininfluente, in tal caso tipicamente si mette proprio <Void>.

Ci sono svariati esempi che si potrebbero fare, uno in particolare è con la classe SwingWorker del framework grafico Swing. SwingWorker serve per eseguire un task in background nel contesto di un thread diverso dal Event Dispatch Thread di Swing, in modo che quest’ultimo non resti “impegnato” a lungo così da mantenere responsiva la interfaccia utente.

SwingWorker è dichiarato come SwingWorker<T,V> dove le due type variable T e V hanno il seguente significato:

  • T rappresenta il tipo del risultato “complessivo” fornito in uscita dal metodo doInBackground e poi ottenibile dal metodo get.
  • V rappresenta il tipo del risultato “intermedio” che il metodo publish può passare (indirettamente) al metodo process.

Senza entrare troppo nei dettagli del funzionamento di SwingWorker, ci potrebbe essere una situazione in cui il risultato complessivo non è importante/necessario. Magari il task di background deve fare solo un certo numero di passi di elaborazione e mostrare lo stato di avanzamento su di una label (JLabel). Allora il codice del task (abbozzato, giusto per far capire) potrebbe essere il seguente:

public class MyWorker extends SwingWorker<Void, String> {
    private JLabel statusLabel;

    public MyWorker(JLabel statusLabel) {
        this.statusLabel = statusLabel;
    }

    @Override
    protected Void doInBackground() throws Exception {
        int n = //........

        for (int i = 0; i < n; i++) {
            publish(String.format("Passo %d di %d", i+1, n));

            // elaborazione lunga ......
        }

        publish("Finito!");
        return null;
    }

    @Override
    protected void process(List<String> chunks) {
        for (String chunk : chunks) {
            statusLabel.setText(chunk);
        }
    }
}

La classe estende SwingWorker<Void, String> e il Void nella prima parametrizzazione serve proprio per indicare che il tipo del risultato complessivo è ininfluente. Questo però è tecnicamente anche il tipo restituito dal doInBackground e infatti si vede che è definito:

protected Void doInBackground() throws Exception {

e il valore restituito è banalmente un null letterale (non potrebbe essere altro) che presumibilmente non verrà usato altrove:

return null;

Conclusioni

Per riassumere l’utilizzo di void e Void:

  • void è una parola chiave del linguaggio e non è un tipo di dato. Il suo scopo principale è dichiarare che un metodo non ha un valore di ritorno. Può essere usato, come caso particolare, nel class literal void.class che è utile praticamente solo nel contesto della reflection di Java.

  • Void è una classe del framework e serve a rappresentare il void come tipo reference. Il suo utilizzo principale è nelle parametrizzazioni dei tipi generici. Se si osserva in un codice una parametrizzazione <Void>, di norma è perché tale parametrizzazione non serve o comunque non è importante.