Ereditarietà: Il Pilastro della Riusabilità
🎯 Obiettivi della lezione
- Comprendere il concetto di ereditarietà
- Creare gerarchie di classi con extends
- Usare la parola chiave super
- Capire protected e la visibilità nelle sottoclassi
- Override di metodi ereditati
💡 Cos'è l'Ereditarietà?
L'ereditarietà permette di creare una nuova classe basata su una classe esistente.
La nuova classe (chiamata sottoclasse o classe figlia) eredita attributi e metodi dalla classe esistente (chiamata superclasse o classe padre).
È come dire: "X è un tipo di Y" (es: un Cane è un tipo di Animale)
🌳 Gerarchia di Classi
Immagina questa situazione:
Scenario: Sistema di gestione veicoli
Vogliamo gestire diversi tipi di veicoli:
- Tutti i veicoli hanno: marca, modello, anno
- Le Auto hanno in più: numero porte
- Le Moto hanno in più: tipo di motore
- I Camion hanno in più: capacità di carico
Invece di ripetere marca, modello, anno in ogni classe, usiamo l'ereditarietà!
📝 Esempio Base: Veicolo e Auto
// SUPERCLASSE (classe padre)
public class Veicolo {
// Attributi comuni a tutti i veicoli
protected String marca;
protected String modello;
protected int anno;
// Costruttore
public Veicolo(String marca, String modello, int anno) {
this.marca = marca;
this.modello = modello;
this.anno = anno;
}
// Metodi comuni
public void mostraInfo() {
System.out.println("Marca: " + marca);
System.out.println("Modello: " + modello);
System.out.println("Anno: " + anno);
}
public void avvia() {
System.out.println("Il veicolo è stato avviato.");
}
}
// SOTTOCLASSE (classe figlia)
public class Auto extends Veicolo {
// Attributo specifico per Auto
private int numeroPorte;
// Costruttore
public Auto(String marca, String modello, int anno, int numeroPorte) {
super(marca, modello, anno); // Chiama costruttore della superclasse
this.numeroPorte = numeroPorte;
}
// Metodo specifico per Auto
public void apriPorta() {
System.out.println("Porta aperta!");
}
// Override del metodo della superclasse
@Override
public void mostraInfo() {
super.mostraInfo(); // Chiama metodo del padre
System.out.println("Numero porte: " + numeroPorte);
}
}
🧪 Usare l'Ereditarietà
public class TestVeicoli {
public static void main(String[] args) {
// Creare un'Auto
Auto miaAuto = new Auto("Fiat", "500", 2020, 3);
System.out.println("=== Informazioni Auto ===");
miaAuto.mostraInfo(); // Metodo override
miaAuto.avvia(); // Metodo ereditato
miaAuto.apriPorta(); // Metodo specifico di Auto
System.out.println();
// Creare un Veicolo generico
Veicolo veicoloGenerico = new Veicolo("Honda", "CBR", 2021);
System.out.println("=== Informazioni Veicolo ===");
veicoloGenerico.mostraInfo();
veicoloGenerico.avvia();
// veicoloGenerico.apriPorta(); // ERRORE! Non esiste in Veicolo
}
}
🔑 Parole Chiave Importanti
extends
class Auto extends Veicolo
Indica che Auto eredita da Veicolo. Auto "è un tipo di" Veicolo.
super
super(parametri) - chiama il costruttore della superclasse
super.metodo() - chiama un metodo della superclasse
IMPORTANTE: super() deve essere la PRIMA istruzione nel costruttore!
protected
Un modificatore di accesso che permette l'accesso:
- Dalla classe stessa
- Dalle sottoclassi
- Da altre classi nello stesso package
È un "via di mezzo" tra private e public, usato spesso con l'ereditarietà.
🏗️ Gerarchia Completa
// SUPERCLASSE
public class Veicolo {
protected String marca;
protected String modello;
protected int anno;
protected double velocitaMassima;
public Veicolo(String marca, String modello, int anno, double velocitaMassima) {
this.marca = marca;
this.modello = modello;
this.anno = anno;
this.velocitaMassima = velocitaMassima;
}
public void mostraInfo() {
System.out.println("=== Informazioni Veicolo ===");
System.out.println("Marca: " + marca);
System.out.println("Modello: " + modello);
System.out.println("Anno: " + anno);
System.out.println("Velocità max: " + velocitaMassima + " km/h");
}
public void avvia() {
System.out.println(marca + " " + modello + " avviato!");
}
}
// SOTTOCLASSE 1: Auto
public class Auto extends Veicolo {
private int numeroPorte;
private String tipologia; // berlina, suv, city car, etc.
public Auto(String marca, String modello, int anno, double velocitaMassima,
int numeroPorte, String tipologia) {
super(marca, modello, anno, velocitaMassima);
this.numeroPorte = numeroPorte;
this.tipologia = tipologia;
}
@Override
public void mostraInfo() {
super.mostraInfo();
System.out.println("Numero porte: " + numeroPorte);
System.out.println("Tipologia: " + tipologia);
}
public void apriPortabagagli() {
System.out.println("Portabagagli aperto!");
}
}
// SOTTOCLASSE 2: Moto
public class Moto extends Veicolo {
private String tipoMotore;
private boolean hasBauletto;
public Moto(String marca, String modello, int anno, double velocitaMassima,
String tipoMotore, boolean hasBauletto) {
super(marca, modello, anno, velocitaMassima);
this.tipoMotore = tipoMotore;
this.hasBauletto = hasBauletto;
}
@Override
public void mostraInfo() {
super.mostraInfo();
System.out.println("Tipo motore: " + tipoMotore);
System.out.println("Bauletto: " + (hasBauletto ? "Sì" : "No"));
}
public void impennaRuota() {
System.out.println("Impennata! 🏍️");
}
}
// SOTTOCLASSE 3: Camion
public class Camion extends Veicolo {
private double capacitaCaricoTon;
private int numeroAssi;
public Camion(String marca, String modello, int anno, double velocitaMassima,
double capacitaCaricoTon, int numeroAssi) {
super(marca, modello, anno, velocitaMassima);
this.capacitaCaricoTon = capacitaCaricoTon;
this.numeroAssi = numeroAssi;
}
@Override
public void mostraInfo() {
super.mostraInfo();
System.out.println("Capacità carico: " + capacitaCaricoTon + " tonnellate");
System.out.println("Numero assi: " + numeroAssi);
}
public void carica(double tonnellate) {
if (tonnellate <= capacitaCaricoTon) {
System.out.println("Caricate " + tonnellate + " tonnellate.");
} else {
System.out.println("Carico troppo pesante!");
}
}
}
🧪 Test Completo
public class TestGerarchiaVeicoli {
public static void main(String[] args) {
// Creare diversi veicoli
Auto auto = new Auto("Fiat", "Panda", 2022, 160, 5, "City Car");
Moto moto = new Moto("Ducati", "Monster", 2023, 240, "4 cilindri", true);
Camion camion = new Camion("Iveco", "Daily", 2021, 120, 3.5, 2);
// Auto
auto.mostraInfo();
auto.avvia();
auto.apriPortabagagli();
System.out.println();
// Moto
moto.mostraInfo();
moto.avvia();
moto.impennaRuota();
System.out.println();
// Camion
camion.mostraInfo();
camion.avvia();
camion.carica(3.0);
camion.carica(5.0);
}
}
📖 Esempio: Persone e Studenti
// Superclasse
public class Persona {
protected String nome;
protected String cognome;
protected int eta;
public Persona(String nome, String cognome, int eta) {
this.nome = nome;
this.cognome = cognome;
this.eta = eta;
}
public void presenta() {
System.out.println("Ciao, sono " + nome + " " + cognome);
}
public String getNomeCompleto() {
return nome + " " + cognome;
}
public int getEta() {
return eta;
}
}
// Sottoclasse
public class Studente extends Persona {
private String matricola;
private String corso;
private double mediaVoti;
public Studente(String nome, String cognome, int eta,
String matricola, String corso) {
super(nome, cognome, eta);
this.matricola = matricola;
this.corso = corso;
this.mediaVoti = 0.0;
}
public void aggiungiVoto(double voto) {
// Semplificato: in realtà dovresti calcolare la media vera
mediaVoti = (mediaVoti + voto) / 2;
}
@Override
public void presenta() {
super.presenta(); // Chiama presenta() di Persona
System.out.println("Sono uno studente di " + corso);
System.out.println("Matricola: " + matricola);
}
public boolean ePromosso() {
return mediaVoti >= 6.0;
}
public void mostraInfo() {
System.out.println("Studente: " + getNomeCompleto());
System.out.println("Età: " + getEta());
System.out.println("Corso: " + corso);
System.out.println("Media: " + mediaVoti);
}
}
// Altra sottoclasse
public class Docente extends Persona {
private String materia;
private int anniServizio;
public Docente(String nome, String cognome, int eta,
String materia, int anniServizio) {
super(nome, cognome, eta);
this.materia = materia;
this.anniServizio = anniServizio;
}
@Override
public void presenta() {
super.presenta();
System.out.println("Insegno " + materia);
System.out.println("Ho " + anniServizio + " anni di servizio");
}
public void faiLezione() {
System.out.println("Il prof. " + cognome + " sta facendo lezione di " + materia);
}
}
public class TestScuola {
public static void main(String[] args) {
Studente studente = new Studente("Mario", "Rossi", 17, "12345", "Informatica");
Docente docente = new Docente("Laura", "Bianchi", 45, "Matematica", 20);
studente.presenta();
studente.aggiungiVoto(8);
studente.aggiungiVoto(7);
studente.mostraInfo();
System.out.println("Promosso: " + studente.ePromosso());
System.out.println("\n---\n");
docente.presenta();
docente.faiLezione();
}
}
⚠️ Regole dell'Ereditarietà
📋 Regole Importanti:
- In Java una classe può ereditare da UNA SOLA classe (no ereditarietà multipla)
- Una classe può avere MOLTE sottoclassi
- L'ereditarietà è transitiva: se C eredita da B e B da A, allora C eredita anche da A
- I costruttori NON vengono ereditati
- I metodi private NON vengono ereditati
- Tutti gli oggetti ereditano da Object (la classe radice di Java)
🎨 Override dei Metodi
L'override permette a una sottoclasse di fornire una propria implementazione di un metodo ereditato.
Usa l'annotazione @Override per chiarezza (opzionale ma consigliato).
public class Animale {
public void emettiVerso() {
System.out.println("L'animale emette un verso");
}
}
public class Cane extends Animale {
@Override
public void emettiVerso() {
System.out.println("Bau bau! 🐕");
}
}
public class Gatto extends Animale {
@Override
public void emettiVerso() {
System.out.println("Miao! 🐱");
}
}
public class Mucca extends Animale {
@Override
public void emettiVerso() {
System.out.println("Muuu! 🐄");
}
}
// Test
Animale a1 = new Cane();
Animale a2 = new Gatto();
Animale a3 = new Mucca();
a1.emettiVerso(); // Bau bau! 🐕
a2.emettiVerso(); // Miao! 🐱
a3.emettiVerso(); // Muuu! 🐄
✏️ Esercizi Guidati
Esercizio 1: Forme Geometriche
MEDIOObiettivo: Creare una gerarchia di forme geometriche.
Requisiti:
- Crea una superclasse Forma con:
- Attributo protected: colore
- Metodo: mostraInfo()
- Crea sottoclasse Rettangolo con:
- Attributi: base, altezza
- Metodo: calcolaArea()
- Override mostraInfo()
- Crea sottoclasse Cerchio con:
- Attributo: raggio
- Metodo: calcolaArea()
- Override mostraInfo()
Esercizio 2: Dipendenti Azienda
MEDIOObiettivo: Gestire diversi tipi di dipendenti.
Requisiti:
- Superclasse Dipendente: nome, cognome, stipendioBase
- Sottoclasse Manager: aggiunge bonus
- Sottoclasse Operaio: aggiunge oreStraordinario
- Metodo calcolaStipendio() diverso per ogni tipo
💪 Esercizi da Svolgere
Esercizio 3: Animali Domestici
FACILECrea gerarchia Animale → Cane/Gatto con:
- Attributi comuni: nome, età
- Metodo emettiVerso() diverso per ogni animale
- Metodi specifici: abbaia(), miagola()
Esercizio 4: Conti Bancari
MEDIOCrea ContoBancario → ContoCorrente/ContoRisparmio:
- ContoCorrente: con fido
- ContoRisparmio: con tasso interesse
- Metodi preleva() diversi
Esercizio 5: Sistema Biblioteca
DIFFICILECrea ElementoBiblioteca → Libro/Rivista/DVD:
- Attributi comuni: titolo, anno
- Libro: autore, pagine
- Rivista: numero, mese
- DVD: durata, regista
- Metodo mostraDettagli() per tutti
🎯 Concetti Chiave da Ricordare
- extends crea una relazione "è un tipo di"
- super() chiama il costruttore del padre (prima istruzione!)
- super.metodo() chiama metodo del padre
- protected = accessibile nelle sottoclassi
- @Override = ridefinisci metodo ereditato
- Solo ereditarietà singola in Java
- Riusa il codice ed evita duplicazioni!