CodeGym /Corsi /JAVA 25 SELF /Uso di super: chiamare il costruttore e i metodi della cl...

Uso di super: chiamare il costruttore e i metodi della classe base

JAVA 25 SELF
Livello 17, Lezione 2
Disponibile

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.

1
Compito
JAVA 25 SELF, livello 17, lezione 2
Bloccato
Suoni della natura: Dal generale al particolare 🐺
Suoni della natura: Dal generale al particolare 🐺
1
Compito
JAVA 25 SELF, livello 17, lezione 2
Bloccato
Linea di produzione: Assemblaggio di veicoli e auto 🏭
Linea di produzione: Assemblaggio di veicoli e auto 🏭
1
Compito
JAVA 25 SELF, livello 17, lezione 2
Bloccato
Albero genealogico dell'animale domestico: Nomi a livelli diversi 🐾
Albero genealogico dell'animale domestico: Nomi a livelli diversi 🐾
1
Compito
JAVA 25 SELF, livello 17, lezione 2
Bloccato
Sistema universitario: Registrazione studenti 👨‍🎓
Sistema universitario: Registrazione studenti 👨‍🎓
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION