Invocare Costruttori Sovraccaricati in Java

Concetti Chiave
  • La parola chiave this() permette di invocare un altro costruttore all'interno di un costruttore in Java.
  • Utilizzare this() può ridurre la duplicazione del codice e semplificare la creazione di oggetti.
  • I costruttori che invocano this() devono essere la prima istruzione nel costruttore.
  • Non si può utilizzare variabili di istanza della classe del costruttore in una chiamata a this().
  • Non si può usare super() e this() nello stesso costruttore perché ognuno deve essere la prima istruzione nel costruttore.

Invocare Costruttori Sovraccaricati

Quando si lavora con costruttori sovraccaricati, è talvolta utile che un costruttore invochi un altro. In Java, questo viene realizzato utilizzando un'altra forma della parola chiave this. La forma generale è mostrata qui:

this(lista_argomenti);

Quando this() viene eseguito, il costruttore sovraccaricato che corrisponde alla lista di parametri specificata da lista_argomenti viene eseguito per primo. Poi, se ci sono delle istruzioni all'interno del costruttore originale, vengono eseguite. La chiamata a this() deve essere la prima istruzione all'interno del costruttore.

Per capire come this() può essere utilizzato, si lavora attraverso un breve esempio. Prima, si considera la seguente classe che non usa this():

class MiaClasse {

    int a;
    int b;

    // inizializza a e b individualmente
    MiaClasse(int i, int j) {
        a = i;
        b = j;
    }

    // inizializza a e b allo stesso valore
    MiaClasse(int i) {
        a = i;
        b = i;
    }

    // assegna ad a e b valori predefiniti di 0
    MiaClasse() {
        a = 0;
        b = 0;
    }

}

Questa classe contiene tre costruttori, ognuno dei quali inizializza i valori di a e b. Il primo riceve valori individuali per a e b. Il secondo riceve solo un valore, che viene assegnato sia ad a che a b. Il terzo assegna ad a e b valori predefiniti di zero.

Utilizzando this(), è possibile riscrivere MiaClasse come mostrato qui:

class MiaClasse {

    int a;
    int b;

    // inizializza a e b individualmente
    MiaClasse(int i, int j) {
        a = i;
        b = j;
    }

    // inizializza a e b allo stesso valore
    MiaClasse(int i) {
        this(i, i); // invoca MiaClasse(i, i)
    }

    // assegna ad a e b valori predefiniti di 0
    MiaClasse() {
        this(0); // invoca MiaClasse(0)
    }

}

In questa versione di MiaClasse, l'unico costruttore che effettivamente assegna valori ai campi a e b è MiaClasse(int, int). Gli altri due costruttori semplicemente invocano quel costruttore (direttamente o indirettamente) attraverso this(). Ad esempio, si consideri cosa accade quando questa istruzione viene eseguita:

MiaClasse mc = new MiaClasse(8);

La chiamata a MiaClasse(8) causa l'esecuzione di this(8, 8), che si traduce in una chiamata a MiaClasse(8, 8), perché questa è la versione del costruttore MiaClasse la cui lista di parametri corrisponde agli argomenti passati tramite this(). Ora, si consideri la seguente istruzione, che usa il costruttore predefinito:

MiaClasse mc2 = new MiaClasse();

In questo caso, this(0) viene chiamato. Questo causa l'invocazione di MiaClasse(0) perché è il costruttore con la lista di parametri corrispondente. Naturalmente, MiaClasse(0) poi chiama MiaClasse(0,0) come appena descritto.

Una ragione per cui invocare costruttori sovraccaricati attraverso this() può essere utile è che può prevenire la duplicazione non necessaria di codice. In molti casi, ridurre il codice duplicato diminuisce il tempo necessario per caricare la classe perché spesso il codice oggetto è più piccolo. Questo è particolarmente importante per programmi distribuiti via Internet in cui i tempi di caricamento sono un problema. Utilizzare this() può anche aiutare a strutturare il codice quando i costruttori contengono una grande quantità di codice duplicato.

Tuttavia, occorre essere attenti. I costruttori che chiamano this() si eseguiranno un po' più lentamente di quelli che contengono tutto il loro codice di inizializzazione inline. Questo è perché il meccanismo di chiamata e ritorno utilizzato quando il secondo costruttore viene invocato aggiunge overhead. Se la classe verrà utilizzata per creare solo una manciata di oggetti, o se i costruttori nella classe che chiamano this() verranno usati raramente, allora questa diminuzione nelle prestazioni di runtime è probabilmente trascurabile. Tuttavia, se la classe verrà utilizzata per creare un grande numero di oggetti (nell'ordine delle migliaia) durante l'esecuzione del programma, allora l'impatto negativo dell'aumento dell'overhead potrebbe essere significativo. Poiché la creazione di oggetti influenza tutti gli utenti della classe, ci saranno casi in cui occorrerà valutare attentamente i benefici di un tempo di caricamento più veloce contro l'aumento del tempo necessario per creare un oggetto.

Ecco un'altra considerazione: per costruttori molto corti, come quelli utilizzati da MiaClasse, spesso c'è poca differenza nella dimensione del codice oggetto sia che this() venga utilizzato o meno. (In realtà, ci sono casi in cui non si ottiene alcuna riduzione nella dimensione del codice oggetto.) Questo è perché il bytecode che imposta e ritorna dalla chiamata a this() aggiunge istruzioni al file oggetto. Pertanto, in questi tipi di situazioni, anche se il codice duplicato viene eliminato, utilizzare this() non otterrà risparmi significativi in termini di tempo di caricamento. Tuttavia, il costo aggiunto in termini di overhead per la costruzione di ogni oggetto sarà comunque sostenuto. Pertanto, this() è più applicabile a costruttori che contengono grandi quantità di codice di inizializzazione, non quelli che semplicemente impostano il valore di una manciata di campi.

Ci sono due restrizioni che occorre tenere a mente quando si usa this(). Prima, non si può utilizzare alcuna variabile di istanza della classe del costruttore in una chiamata a this(). Seconda, non si può utilizzare super() e this() nello stesso costruttore perché ognuno deve essere la prima istruzione nel costruttore.