CodeGym /Corsi /JAVA 25 SELF /Il concetto di ereditarietà, sintassi di extends

Il concetto di ereditarietà, sintassi di extends

JAVA 25 SELF
Livello 17 , Lezione 0
Disponibile

1. Che cos’è l’ereditarietà?

L’ereditarietà è un meccanismo che consente di creare una nuova classe a partire da una classe già esistente, «ereditandone» proprietà e metodi. La nuova classe si chiama sottoclasse (o classe figlia), mentre l’originale è la classe genitore (o classe base).

È come preparare una nuova torta partendo da una vecchia ricetta: prendi tutto il meglio che c’è, aggiungi un paio di ingredienti nuovi — e voilà, hai già la «Torta della nonna con ingrediente segreto».

  • Classe genitore — contiene proprietà e comportamenti comuni.
  • Sottoclasse — eredita tutto questo e può aggiungere qualcosa di proprio o modificare il comportamento.

Ecco, per esempio, «Il gatto è un animale»: tutti i gatti sono animali, ma non tutti gli animali sono gatti. Animal può essere la classe base e Cat la sua sottoclasse. Anche Dog, per esempio.
Oppure «Il cerchio è una figura»: ogni cerchio è una figura, ma non ogni figura è un cerchio. Figure può essere la classe genitore e Cerchio e Triangolo — i suoi eredi. In Java questi legami sono realizzati tramite la parola chiave extends.

Perché serve l’ereditarietà?

Domanda da un milione: perché non copiare semplicemente il codice da una classe all’altra? Perché è stato inventato proprio il meccanismo dell’ereditarietà?

  • Riutilizzo del codice: tutto ciò che è comune si sposta nella classe base. Non serve duplicare campi e metodi — compaiono automaticamente nelle sottoclassi. Per esempio, in un programma per l’università la classe base può essere «persona», che ha almeno nome e data di nascita; «studente» e «docente» sono sottoclassi con proprietà proprie.
  • Estensione e specializzazione: puoi aggiungere nuovi campi e metodi o modificare quelli esistenti solo dove serve. Tutti gli animali emettono suoni, ma il gatto miagola e il cane abbaia. Al gatto puoi aggiungere l’abilità di arrampicarsi sugli alberi (abilità che la classe base non ha).
  • Strutturazione di gerarchie: il codice diventa più logico e comprensibile. Ad esempio, se hai le classi Animal, Dog, Cat, è subito chiaro che Dog e Cat sono casi particolari di Animal.

Esempio. Se in Java non ci fosse l’ereditarietà, saresti costretto a copiare metodi come eat(), sleep() e il campo name in ogni classe di animale. E se domani servisse aggiungere il metodo breathe(), dovresti modificare decine di classi! Con l’ereditarietà basta aggiungere la capacità di respirare nella classe base.

2. Sintassi dell’ereditarietà: extends

Come dichiarare l’ereditarietà in Java? È tutto molto semplice: usa la parola chiave extends.

class RoditelskiyKlass {
    // campi e metodi
}

class Podklass extends RoditelskiyKlass {
    // campi e metodi aggiuntivi
}

Esempio 1: Animali

// Classe base
class Animal {
    String name;

    void eat() {
        System.out.println(name + " mangia.");
    }
}

// Sottoclasse
class Cat extends Animal {
    void meow() {
        System.out.println(name + " dice: Miao!");
    }
}

Che cosa succede qui?

  • La classe Cat eredita tutti i campi e i metodi della classe Animal.
  • Quindi Cat ha già il campo name e il metodo eat(), anche se non li abbiamo scritti esplicitamente.
  • In più Cat introduce il suo metodo meow().

Utilizzo

public class Main {
    public static void main(String[] args) {
        Cat kitty = new Cat();
        kitty.name = "Barsik";
        kitty.eat();      // Barsik mangia.  (ereditato)
        kitty.meow();     // Barsik dice: Miao! (metodo proprio)
    }
}

Voilà! Abbiamo ottenuto una nuova classe con il minimo codice, evitando duplicazioni.

3. Che cosa eredita esattamente una sottoclasse?

In Java una sottoclasse eredita:

  • Tutti i campi e metodi public e protected della classe base.
  • Tutti i membri package-private, se la sottoclasse è nello stesso package.

Non eredita:

  • I costruttori (vanno dichiarati esplicitamente nella sottoclasse).
  • I campi e i metodi private (sono accessibili solo all’interno della classe base stessa).
  • I blocchi di inizializzazione statici (non si ereditano, ma ogni classe può avere i propri).
  • I membri con accesso package-private, se la sottoclasse si trova in un altro package.

Illustrazione

class Animal {
    public String name;
    private int age;

    public void eat() { 
        System.out.println("Mangio"); 
    }
    private void secret() { 
        System.out.println("Segreto!"); 
    }
}

class Dog extends Animal {
    public void bark() { 
        System.out.println("Bau!"); 
    }
}
  • Dog eredita il campo name e il metodo eat().
  • Dog non vede il campo age e il metodo secret() — sono privati.

4. Limitazioni dell’ereditarietà in Java

Java è una signora piuttosto severa, con le sue regole:

Solo ereditarietà singola tra classi: ogni classe può estendere una sola altra classe. In compenso, il numero di eredi di una classe non ha limiti.

class Dog extends Animal { ... } // Ok
class Dog extends Animal, Pet { ... } // Errore! Non si può fare

Ereditarietà multipla di interfacce — si può, ma è un’altra storia e ne parleremo più avanti.

La classe Object — è l’antenata di tutte le classi in Java. Anche se non scrivi esplicitamente extends, la tua classe eredita comunque implicitamente da Object.

5. Esempio pratico

Continuiamo a costruire la nostra app «zoologica».

Passo 1. Classe base Animal

class Animal {
    String name;

    void eat() {
        System.out.println(name + " mangia.");
    }
}

Passo 2. Sottoclasse Cat

class Cat extends Animal {
    void meow() {
        System.out.println(name + " dice: Miao!");
    }
}

Passo 3. Sottoclasse Dog

class Dog extends Animal {
    void bark() {
        System.out.println(name + " dice: Bau!");
    }
}

Passo 4. Usiamo le classi

public class Main {
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.name = "Murka";
        cat.eat();   // Murka mangia.
        cat.meow();  // Murka dice: Miao!

        Dog dog = new Dog();
        dog.name = "Sharik";
        dog.eat();   // Sharik mangia.
        dog.bark();  // Sharik dice: Bau!
    }
}

Che cosa abbiamo ottenuto?

  • Entrambe le classi Cat e Dog usano il campo comune name e il metodo eat() dalla classe base.
  • Ogni classe aggiunge un comportamento unico: meow() per il gatto, bark() per il cane.
  • Se serve cambiare il modo in cui gli animali mangiano, basta modificare il metodo eat() nella classe Animal — le modifiche si applicheranno automaticamente a tutti gli eredi.

6. Dettagli utili

Visualizzazione: gerarchia delle classi

Possiamo rappresentare la gerarchia così:

.        Animal
        /    \
     Cat     Dog
  • Animal — classe base, contiene tutto ciò che è comune agli animali.
  • Cat e Dog — sottoclassi, aggiungono caratteristiche uniche.

Un po’ sui costruttori e l’ereditarietà

I costruttori non si ereditano. Se non definisci un costruttore nella sottoclasse, Java aggiungerà automaticamente un costruttore senza parametri (se esiste nel genitore). Se la classe genitore ha solo un costruttore con parametri, nella sottoclasse va chiamato esplicitamente tramite super(...). Breve esempio:

class Animal {
    String name;
    Animal(String name) {
        this.name = name;
    }
}

class Cat extends Animal {
    Cat(String name) {
        super(name); // chiamiamo il costruttore del genitore
    }
}

7. Errori tipici nell’uso dell’ereditarietà

Errore n. 1: tentativo di ereditare da più classi contemporaneamente.
In Java non si può scrivere class Cat extends Animal, Pet. Solo un genitore!

Errore n. 2: aspettarsi che i campi e i metodi privati siano accessibili nella sottoclasse.
I membri privati non si ereditano (o meglio: esistono, ma non sono accessibili dalla sottoclasse). Usa protected se vuoi consentire l’accesso agli eredi.

Errore n. 3: dimenticare la chiamata al costruttore del genitore.
Se nella classe base non c’è un costruttore senza parametri e non chiami esplicitamente super(...), otterrai un errore di compilazione.

Errore n. 4: usare l’ereditarietà «per risparmiare codice» e non per una relazione semantica.
Se tra le classi non c’è una relazione «è un» (is-a), è meglio non usare l’ereditarietà. Ad esempio, «Persona» non dovrebbe ereditare da «Automobile», anche se entrambe hanno il campo speed.

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION