Utilizzo di super in Java
- La parola chiave
super
in Java consente di accedere ai membri della superclasse e di chiamare i suoi costruttori. super()
deve essere la prima istruzione in un costruttore di sottoclasse.super
può essere utilizzato per accedere a variabili e metodi della superclasse che sono stati oscurati nella sottoclasse.- È possibile passare un oggetto della sottoclasse a un costruttore della superclasse, consentendo l'accesso ai membri della superclasse.
Utilizzo di super
Nei precedenti esempi le classi derivate da Scatola
non erano state implementate nel modo più efficiente e robusto possibile.
Per esempio, il costruttore di ScatolaPeso
inizializzava esplicitamente i campi larghezza
, altezza
e profondità
definiti in Scatola
. In questo modo si duplicava codice già presente nella superclasse, con conseguente inefficienza, e si lasciava intendere che la sottoclasse dovesse poter accedere a tali membri.
Esistono però situazioni in cui vogliamo creare una superclasse che mantenga i dettagli della propria implementazione (ovvero i propri dati) privati. In tal caso la sottoclasse non avrebbe modo di accedere o inizializzare direttamente tali variabili.
Poiché l'incapsulamento è un attributo fondamentale della programmazione orientata agli oggetti, non sorprende che Java offra una soluzione. Ogni volta che una sottoclasse deve riferirsi alla propria superclasse immediata, può farlo mediante la parola chiave super
.
super
presenta due forme generali:
- Chiamare il costruttore della superclasse.
- Accedere a un membro della superclasse che è stato oscurato da un membro della sottoclasse.
Di seguito analizziamo entrambi gli utilizzi.
Utilizzo di super
per chiamare i costruttori della superclasse
Una sottoclasse può invocare un costruttore definito nella superclasse tramite la forma:
super(elencoArgomenti);
dove elencoArgomenti
specifica gli argomenti richiesti dal costruttore della superclasse. super()
deve sempre essere la prima istruzione eseguita all'interno del costruttore della sottoclasse.
Per comprendere l'uso di super()
, consideriamo questa versione migliorata di ScatolaPeso
:
// ScatolaPeso ora usa super per inizializzare gli attributi di Scatola.
class ScatolaPeso extends Scatola {
double peso; // peso della scatola
// inizializza larghezza, altezza e profondità tramite super()
ScatolaPeso(double w, double h, double d, double m) {
super(w, h, d); // chiama il costruttore della superclasse
peso = m;
}
}
ScatolaPeso()
invoca super()
con gli argomenti w
, h
e d
, attivando così il costruttore di Scatola
, che inizializza larghezza
, altezza
e profondità
. ScatolaPeso
si limita a inizializzare il campo specifico peso
, lasciando alla superclasse la gestione dei propri dati e rendendo possibile dichiararli private
se necessario.
Nell'esempio precedente super()
è stato chiamato con tre argomenti. Poiché i costruttori possono essere sovraccaricati, super()
può essere invocato con qualunque forma definita nella superclasse: verrà eseguito il costruttore che corrisponde alla lista di argomenti.
Di seguito presentiamo un'implementazione completa di ScatolaPeso
che fornisce costruttori per tutti i modi in cui è possibile creare una scatola; in ogni caso super()
viene chiamato con gli argomenti appropriati. Si noti che larghezza
, altezza
e profondità
sono stati resi private
in Scatola
.
// Implementazione completa di Scatola e ScatolaPeso.
class Scatola {
private double larghezza;
private double altezza;
private double profondità;
// costruttore copia
Scatola(Scatola ob) { // passa l'oggetto al costruttore
larghezza = ob.larghezza;
altezza = ob.altezza;
profondità = ob.profondità;
}
// costruttore con tutte le dimensioni specificate
Scatola(double w, double h, double d) {
larghezza = w;
altezza = h;
profondità = d;
}
// costruttore senza dimensioni specificate
Scatola() {
larghezza = -1; // -1 indica non inizializzato
altezza = -1;
profondità = -1;
}
// costruttore per un cubo
Scatola(double lato) {
larghezza = altezza = profondità = lato;
}
// calcola e restituisce il volume
double volume() {
return larghezza * altezza * profondità;
}
}
// ScatolaPeso implementa tutti i costruttori.
class ScatolaPeso extends Scatola {
double peso; // peso della scatola
// costruttore copia
ScatolaPeso(ScatolaPeso ob) { // passa l'oggetto al costruttore
super(ob);
peso = ob.peso;
}
// costruttore con tutti i parametri specificati
ScatolaPeso(double w, double h, double d, double m) {
super(w, h, d); // chiama il costruttore della superclasse
peso = m;
}
// costruttore di default
ScatolaPeso() {
super();
peso = -1;
}
// costruttore per un cubo
ScatolaPeso(double lato, double m) {
super(lato);
peso = m;
}
}
class DimostrazioneSuper {
public static void main(String[] args) {
ScatolaPeso miaScatola1 = new ScatolaPeso(10, 20, 15, 34.3);
ScatolaPeso miaScatola2 = new ScatolaPeso(2, 3, 4, 0.076);
ScatolaPeso miaScatola3 = new ScatolaPeso(); // costruttore di default
ScatolaPeso mioCubo = new ScatolaPeso(3, 2);
ScatolaPeso miaCopia = new ScatolaPeso(miaScatola1);
double vol;
vol = miaScatola1.volume();
System.out.println("Il volume di miaScatola1 è " + vol);
System.out.println("Il peso di miaScatola1 è " + miaScatola1.peso);
System.out.println();
vol = miaScatola2.volume();
System.out.println("Il volume di miaScatola2 è " + vol);
System.out.println("Il peso di miaScatola2 è " + miaScatola2.peso);
System.out.println();
vol = miaScatola3.volume();
System.out.println("Il volume di miaScatola3 è " + vol);
System.out.println("Il peso di miaScatola3 è " + miaScatola3.peso);
System.out.println();
vol = miaCopia.volume();
System.out.println("Il volume di miaCopia è " + vol);
System.out.println("Il peso di miaCopia è " + miaCopia.peso);
vol = mioCubo.volume();
System.out.println("Il volume di mioCubo è " + vol);
System.out.println("Il peso di mioCubo è " + mioCubo.peso);
System.out.println();
}
}
Questo programma produce il seguente output:
Il volume di miaScatola1 è 3000.0
Il peso di miaScatola1 è 34.3
Il volume di miaScatola2 è 24.0
Il peso di miaScatola2 è 0.076
Il volume di miaScatola3 è -1.0
Il peso di miaScatola3 è -1.0
Il volume di miaCopia è 3000.0
Il peso di miaCopia è 34.3
Il volume di mioCubo è 27.0
Il peso di mioCubo è 2.0
Si presti particolare attenzione a questo costruttore in ScatolaPeso
:
// crea un clone di un oggetto
ScatolaPeso(ScatolaPeso ob) { // passa l'oggetto al costruttore
super(ob);
peso = ob.peso;
}
Si osservi che a super()
viene passato un oggetto di tipo ScatolaPeso
e non di tipo Scatola
. Ciò invoca comunque il costruttore Scatola(Scatola ob)
. Come detto in precedenza, una variabile di superclasse può fare riferimento a qualunque oggetto derivato da quella classe; è dunque possibile passare a Scatola
un oggetto di tipo ScatolaPeso
. Naturalmente Scatola
conosce soltanto i propri membri.
Rivediamo i concetti chiave relativi a super()
. Quando una sottoclasse chiama super()
, viene invocato il costruttore della sua superclasse immediata. Ne consegue che super()
fa sempre riferimento alla superclasse immediatamente sopra la classe chiamante, anche in gerarchie a più livelli. Inoltre, super()
deve sempre essere la prima istruzione eseguita all'interno di un costruttore di sottoclasse.
Un secondo utilizzo di super
La seconda forma di super
funziona in modo analogo, con la differenza che fa sempre riferimento alla superclasse della sottoclasse in cui viene usata. La sintassi generale è:
super.membro
dove membro
può essere un metodo o una variabile d'istanza.
Questa forma di super
risulta utile quando nella sottoclasse i nomi dei membri nascondono quelli omonimi definiti nella superclasse. Consideriamo la seguente gerarchia di classi:
// Uso di super per superare l'occultamento di nomi.
class A {
int i;
}
// Creiamo una sottoclasse estendendo la classe A.
class B extends A {
int i; // questo i occulta l'i in A
B(int a, int b) {
super.i = a; // i in A
i = b; // i in B
}
void mostra() {
System.out.println("i nella superclasse: " + super.i);
System.out.println("i nella sottoclasse: " + i);
}
}
class UsaSuper {
public static void main(String[] args) {
B sottoOg = new B(1, 2);
sottoOg.mostra();
}
}
Questo programma visualizza:
i nella superclasse: 1
i nella sottoclasse: 2
Sebbene la variabile d'istanza i
in B
occulti quella in A
, super
consente l'accesso all'i
definita nella superclasse. Come si vedrà, super
può essere usato anche per chiamare metodi che risultano nascosti da una sottoclasse.