1. Sintassi di una classe astratta
Vediamo che cos’è una classe astratta in Java e a cosa serve. Immaginate di avere un set di LEGO: avete un insieme di pezzi (ad esempio, «ruote», «mattoncini») e pezzi speciali per modelli specifici. Una classe astratta è come un’istruzione generale su come trattare i pezzi senza descrivere l’assemblaggio di un modello concreto: i mattoncini si agganciano con gli incastri, le ruote si fissano agli assi, i pezzi possono essere sovrapposti a strati. La classe astratta descrive proprio queste regole comuni e i passaggi obbligatori, ma non spiega come costruire un modello specifico. I modelli concreti sono le sottoclassi: prendono l’istruzione comune e aggiungono i passaggi mancanti.
In Java una classe astratta è una classe che non può essere istanziata direttamente (non si può fare new AbstractClass()), ma da cui si può ereditare e in cui si possono implementare i metodi «incompleti».
Le classi astratte sono utili quando:
- Un gruppo di oggetti ha un comportamento o uno stato comune, ma parte della logica differisce.
- Volete implementare parte della funzionalità «di default» e lasciare il resto all’implementazione delle sottoclassi.
- Volete impedire la creazione diretta di oggetti di quella classe (ad esempio, «Animale» di per sé non si incontra, mentre «Gatto» sì).
Come dichiarare una classe astratta
È semplice: prima della parola class si scrive la parola chiave abstract.
public abstract class Animal {
// Campi (ad esempio, il nome dell'animale)
protected String name;
// Costruttore
public Animal(String name) {
this.name = name;
}
// Metodo astratto — solo dichiarazione, senza implementazione!
public abstract void makeSound();
// Metodo normale (implementato)
public void sleep() {
System.out.println(name + " dorme: Zzz...");
}
}
Caratteristiche:
- Una classe astratta può contenere sia metodi implementati sia metodi astratti.
- Una classe astratta può contenere campi, costruttori e anche metodi static.
- Non è possibile creare direttamente un’istanza di una classe astratta:
-
Animal a = new Animal("Qualcuno"); // Errore!
Come dichiarare un metodo astratto
Un metodo astratto è un metodo senza corpo, cioè senza parentesi graffe e codice all’interno. Si dichiara con la parola chiave abstract e termina obbligatoriamente con il punto e virgola ;.
public abstract void makeSound();
- I metodi astratti si possono dichiarare solo all’interno di una classe astratta.
- Una classe che estende una classe astratta deve implementare tutti i metodi astratti, altrimenti anch’essa diventa abstract.
2. Ereditarietà delle classi astratte: come funziona
Vediamo un esempio concreto. Supponiamo di avere una classe astratta Animal con un metodo astratto makeSound(). Creiamo ora una sottoclasse Dog che implementa questo metodo:
public class Dog extends Animal {
public Dog(String name) {
super(name); // Chiamata al costruttore della classe base
}
@Override
public void makeSound() {
System.out.println(name + " abbaia: Bau-bau!");
}
}
E un’altra sottoclasse:
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + " miagola: Miao!");
}
}
Ora possiamo usare queste classi nel programma:
public class Main {
public static void main(String[] args) {
Animal dog = new Dog("Fido");
Animal cat = new Cat("Micia");
dog.makeSound(); // Fido abbaia: Bau-bau!
cat.makeSound(); // Micia miagola: Miao!
dog.sleep(); // Fido dorme: Zzz...
cat.sleep(); // Micia dorme: Zzz...
}
}
Da notare:
Possiamo dichiarare variabili di tipo Animal, ma creare oggetti solo delle sottoclassi concrete (non astratte).
Schema: come appare
. Animal (abstract)
/ \
Dog Cat
(implementa makeSound) (implementa makeSound)
3. Quando usare una classe astratta e quando un’interfaccia?
È una delle domande più popolari ai colloqui, e non a caso! Vediamo di chiarire:
- Classe astratta — quando gli oggetti hanno uno stato comune (ad esempio, campi), una logica comune (metodi con implementazione) e volete fornire un «telaio» di comportamento con possibilità di estensione.
- Interfaccia — quando volete definire solo un insieme di metodi (contratto), senza implementazione e senza stato. Da Java 8 le interfacce hanno ricevuto metodi default/static, ma comunque l’interfaccia riguarda «che cosa deve saper fare un oggetto», non «come lo fa».
Esempio reale:
«Uccello» — classe astratta: tutti gli uccelli hanno becco, ali e possono volare (ma in modi diversi).
«Volante» — interfaccia: non solo gli uccelli sanno volare, ma anche gli aerei e i supereroi! Volano in modi diversi, ma l’importante è che sappiano farlo.
4. Esempi pratici
Esempio 1: classe astratta con implementazione parziale
Supponiamo di creare un gioco con diversi tipi di trasporto. Tutti possono muoversi, ma lo fanno in modo diverso. Tuttavia, tutti hanno una velocità, un nome e un modo standard di fermarsi.
public abstract class Transport {
protected String name;
protected int speed;
public Transport(String name, int speed) {
this.name = name;
this.speed = speed;
}
// Metodo astratto — implementazione nelle sottoclassi
public abstract void move();
// Metodo implementato
public void stop() {
System.out.println(name + " si è fermato.");
}
}
public class Car extends Transport {
public Car(String name, int speed) {
super(name, speed);
}
@Override
public void move() {
System.out.println(name + " viaggia su strada alla velocità di " + speed + " km/h.");
}
}
public class Bicycle extends Transport {
public Bicycle(String name, int speed) {
super(name, speed);
}
@Override
public void move() {
System.out.println(name + " pedala alla velocità di " + speed + " km/h.");
}
}
Uso:
Transport car = new Car("Toyota", 120);
Transport bike = new Bicycle("Stels", 25);
car.move(); // Toyota viaggia su strada alla velocità di 120 km/h.
bike.move(); // Stels pedala alla velocità di 25 km/h.
car.stop(); // Toyota si è fermato.
bike.stop(); // Stels si è fermato.
Esempio 2: metodo astratto con parametro
public abstract class Shape {
public abstract double area();
}
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
public class Rectangle extends Shape {
private double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
}
Uso:
Shape c = new Circle(3);
Shape r = new Rectangle(4, 5);
System.out.println("Area del cerchio: " + c.area());
System.out.println("Area del rettangolo: " + r.area());
5. Peculiarità e sfumature delle classi astratte
- Una classe astratta può usare qualsiasi modificatore di accesso: public, protected, private (ad esempio, per i campi).
- È possibile dichiarare una classe astratta senza metodi astratti. Può essere utile se volete semplicemente vietare l’istanziazione della classe base.
- Se una classe estende una classe astratta ma non implementa tutti i metodi astratti, deve anch’essa essere dichiarata abstract.
- Una classe astratta può avere costruttori. Vengono chiamati alla creazione dell’oggetto derivato (tramite super(...)).
- I metodi astratti non possono essere private (altrimenti non potrebbero essere implementati nella sottoclasse).
- Le classi astratte possono contenere metodi e campi static.
- Una classe astratta può implementare interfacce, ma non è obbligata a implementarne i metodi — possono farlo le sottoclassi.
Tabella: confronto tra classe astratta e interfaccia
| Classe astratta | Interfaccia | |
|---|---|---|
| Parola chiave | |
|
| Può contenere campi | Sì (qualsiasi) | Da Java 8 — solo static/final |
| Metodi con implementazione | Sì | Da Java 8 — default/static |
| Metodi astratti | Sì | Sì |
| Ereditarietà multipla | No | Sì (si possono implementare molte interfacce) |
| Costruttori | Sì | No |
| Si può creare un’istanza | No | No |
6. Errori tipici nell’uso di classi e metodi astratti
Errore n. 1: tentativo di creare un’istanza di una classe astratta.
Se scrivete new Animal("Qualcuno"), il compilatore vi ricorderà subito che le classi astratte non sono fatte per questo. Ricordate: l’astrazione è lo «scheletro», non un «organismo vivente».
Errore n. 2: avete dimenticato di implementare tutti i metodi astratti nella sottoclasse.
Se la vostra classe non implementa almeno un metodo astratto della classe base, deve essere dichiarata abstract, altrimenti otterrete un errore di compilazione.
Errore n. 3: avete dichiarato un metodo astratto fuori da una classe astratta.
In Java non si può dichiarare un metodo astratto in una classe normale (non astratta) — il compilatore si lamenterà subito.
Errore n. 4: tentativo di rendere un metodo astratto private o static.
I metodi astratti non possono essere private (altrimenti non possono essere sovrascritti) e non possono essere static (perché i metodi statici non si sovrascrivono). E non possono essere neanche final, perché final vieta l’override.
Errore n. 5: avete dimenticato il modificatore di accesso per un metodo astratto.
Se non specificate esplicitamente il modificatore, il metodo avrà visibilità di pacchetto (package-private), che a volte non è ciò che desideravate ottenere.
GO TO FULL VERSION