1. Uso di super per chiamare i metodi della classe base
Quando create una sottoclasse, a volte capita di dover accedere ai campi o ai metodi della classe base, soprattutto se li avete sovrascritti o «oscurati» (shadowing) nella sottoclasse. A questo in Java serve la parola chiave speciale — super.
Per analogia, super è come «mamma, aiutami!», quando nella sottoclasse volete riferirvi esplicitamente a ciò che è definito nel genitore.
Immaginate di avere la classe Animal con il metodo eat(), che stampa "L'animale mangia". E nella classe Cat volete che il gatto faccia prima qualcosa di suo (per esempio, miagolare), e poi esegua comunque lo standard «l’animale mangia». Qui torna utile super.eat().
Quando nella sottoclasse sovrascrivete un metodo ma volete comunque chiamare la sua implementazione dalla classe base, usate super.nomeMetodo().
Esempio: estendiamo il comportamento
class Animal {
void eat() {
System.out.println("L'animale mangia");
}
}
class Cat extends Animal {
@Override
void eat() {
System.out.println("Il gatto annusa il cibo...");
super.eat(); // chiamiamo il metodo eat() da Animal
System.out.println("Il gatto fa le fusa soddisfatto");
}
}
Come funziona?
- Quando si chiama eat() su un oggetto di tipo Cat, prima si esegue il codice di Cat.eat(), cioè il metodo della sottoclasse.
- All’interno di questo metodo chiamiamo esplicitamente super.eat(), cioè l’implementazione della classe genitore Animal.
- Questo permette di aggiungere comportamento aggiuntivo senza dimenticare quello «del genitore».
Pratica: usiamolo in un’applicazione
Supponiamo che nella nostra app didattica ci sia la classe base Animal e le sottoclassi Dog e Cat. Vogliamo che durante l’alimentazione si eseguano sia le azioni comuni (per esempio, aumento della sazietà) sia quelle specifiche per ogni animale.
class Animal {
int satiety = 0;
void eat() {
satiety += 10;
System.out.println("L'animale mangia. Sazietà: " + satiety);
}
}
class Dog extends Animal {
@Override
void eat() {
System.out.println("Il cane scodinzola prima di mangiare");
super.eat();
}
}
Ora, se chiamate dog.eat(), vedrete entrambi i messaggi e satiety aumenterà correttamente.
2. Uso di super per accedere ai campi della classe base
Se nella sottoclasse dichiarate un campo con lo stesso nome della classe genitore, esso «oscurerà» il campo del genitore. A volte occorre accedere al campo originale della classe base — per questo serve super.nomeCampo.
Esempio: shadowing di un campo
class Animal {
String name = "Animale";
}
class Cat extends Animal {
String name = "Gatto";
void printNames() {
System.out.println("Nome da Cat: " + name);
System.out.println("Nome da Animal: " + super.name);
}
}
La chiamata new Cat().printNames(); stamperà:
Nome da Cat: Gatto
Nome da Animal: Animale
Nella pratica reale non si consiglia di «oscurare» i campi se non strettamente necessario, ma è utile conoscere questa possibilità.
3. Chiamare il costruttore della classe base tramite super(...)
Come vengono creati gli oggetti in un’ereditarietà?
Quando create un oggetto di una sottoclasse, prima viene chiamato il costruttore della classe base e solo dopo — quello della sottoclasse. Questo serve a inizializzare correttamente tutti i campi, poiché la sottoclasse «eredita» parte dello stato dal genitore.
Chiamata esplicita del costruttore della classe base
Se la classe base ha un costruttore senza parametri — tutto semplice: Java lo chiamerà automaticamente prima di eseguire il costruttore della sottoclasse. Ma se il genitore non ha un costruttore senza parametri, dovete chiamare esplicitamente il costruttore necessario tramite super(...).
Esempio:
class Animal {
String name;
Animal(String name) {
this.name = name;
System.out.println("Creato animale: " + name);
}
}
class Cat extends Animal {
Cat(String name) {
super(name); // obbligatorio! Non esiste Animal() senza parametri
System.out.println("Creato gatto: " + name);
}
}
La chiamata new Cat("Murka") stamperà:
Creato animale: Murka
Creato gatto: Murka
Importante: La chiamata al costruttore del genitore tramite super(...) deve essere la prima riga del costruttore della sottoclasse. Se provate a scrivere qualcosa prima di questa chiamata, il compilatore non sarà d’accordo e ve lo ricorderà.
Se non lo chiamate esplicitamente?
Se il genitore ha solo costruttori con parametri e non lo avete chiamato esplicitamente tramite super(...), il compilatore restituirà un errore: "constructor Animal in class Animal cannot be applied to given types".
4. Sfumature utili
Quando usare super?
Per estendere, non per sostituire il comportamento.
A volte non volete sostituire completamente il comportamento di un metodo, ma solo «estenderlo». Per esempio, aggiungere qualcosa prima o dopo la logica del genitore. In tali casi usate super.nomeMetodo() nel corpo del metodo sovrascritto.
Per inizializzare i campi ereditati.
Se il genitore ha campi che devono essere inizializzati (per esempio, il nome dell’animale), chiamate il costruttore del genitore con i parametri necessari tramite super(...).
Per accedere a campi/metodi nascosti.
Se per qualche motivo avete «oscurato» un campo o un metodo del genitore e dovete comunque accedervi, usate super.nomeCampo o super.nomeMetodo().
Limitazioni e peculiarità dell’uso di super
- La chiamata al costruttore del genitore tramite super(...) si può fare solo nel costruttore e solo come prima riga.
- Non si può chiamare il costruttore del genitore al di fuori del costruttore della sottoclasse.
- Se non si chiama esplicitamente super(...), Java proverà a chiamare il costruttore senza parametri del genitore (se esiste).
- La parola chiave super non si può usare nei metodi statici — solo nei metodi d’istanza e nei costruttori.
- Se il metodo o il campo del genitore è private, allora super non aiuta: i membri privati non sono accessibili.
5. Esempi per fissare le idee
Esempio 1. Estendere un metodo con super
class Animal {
void makeSound() {
System.out.println("L'animale emette un suono");
}
}
class Dog extends Animal {
@Override
void makeSound() {
super.makeSound(); // prima eseguiamo l’azione standard
System.out.println("Il cane abbaia: Bau bau!");
}
}
Esempio 2. Chiamata del costruttore della classe base
class Vehicle {
String brand;
Vehicle(String brand) {
this.brand = brand;
System.out.println("Veicolo: " + brand);
}
}
class Car extends Vehicle {
int year;
Car(String brand, int year) {
super(brand); // chiamiamo il costruttore del genitore
this.year = year;
System.out.println("Auto " + brand + ", anno: " + year);
}
}
Car car = new Car("Toyota", 2023);
// Output:
// Veicolo: Toyota
// Auto Toyota, anno: 2023
Esempio 3. Errore classico: vi siete dimenticati di chiamare super(...)
class Animal {
String name;
Animal(String name) {
this.name = name;
}
}
class Cat extends Animal {
Cat() {
// super(); // Errore! Non esiste il costruttore Animal() senza parametri
// Bisogna chiamare esplicitamente super(name)
super("Gatto senza nome");
}
}
6. Errori tipici nell’uso di super
Errore n. 1: chiamare super(...) non come prima riga del costruttore.
Java richiede rigorosamente che la chiamata al costruttore del genitore tramite super(...) sia la prima riga del costruttore della sottoclasse. Se provate a fare qualcosa prima di questa chiamata (per esempio, stampare un messaggio), il compilatore restituirà un errore.
Errore n. 2: il genitore non ha un costruttore adatto.
Se la classe base non ha un costruttore senza parametri e non avete chiamato un altro costruttore tramite super(...), il compilatore non potrà generare una chiamata predefinita e segnalerà un errore.
Errore n. 3: tentativo di accedere ai membri privati del genitore tramite super.
La parola chiave super non fornisce accesso magico ai campi o ai metodi privati del genitore. Se qualcosa è dichiarato come private, rimane inaccessibile alla sottoclasse.
Errore n. 4: shadowing di campi e metodi senza consapevolezza.
Se nella sottoclasse dichiarate un campo o un metodo con lo stesso nome del genitore e ve ne dimenticate, sono possibili risultati imprevisti. Ricordate sempre che in tal caso l’accesso al membro del genitore avviene solo tramite super.
Errore n. 5: usare super in un metodo statico.
Nei metodi statici non si può usare super, perché non appartengono a un oggetto specifico.
GO TO FULL VERSION