Vai al contenuto
eLearner.app
Modulo 3 · Lezione 2 di 26/14 nel corso~15 min
Lezioni del modulo (2/2)

Polimorfismo e Classi Astratte

Il polimorfismo e le classi astratte sono concetti avanzati della programmazione ad oggetti che consentono di scrivere codice flessibile, estensibile e indipendente dalle specifiche implementazioni.

Il Polimorfismo

Il termine polimorfismo (molte forme) si riferisce alla capacità di trattare oggetti di classi diverse come se appartenessero ad un tipo comune. In Java, una variabile di tipo superclasse può memorizzare un riferimento ad un oggetto di qualsiasi sottoclasse.

Code
class Animal {
    public void makeSound() {
        System.out.println("Verso...");
    }
}

class Dog extends Animal {
    public void makeSound() {
        System.out.println("Woof");
    }
}

class Cat extends Animal {
    public void makeSound() {
        System.out.println("Miao");
    }
}

Grazie al polimorfismo, possiamo fare questo:

Code
Animal myAnimal1 = new Dog(); // Polimorfismo
Animal myAnimal2 = new Cat(); // Polimorfismo

myAnimal1.makeSound(); // Esegue il metodo di Dog (Woof)
myAnimal2.makeSound(); // Esegue il metodo di Cat (Miao)

La decisione su quale metodo invocare avviene a runtime (Late Binding o dynamic dispatch) in base all'oggetto effettivo e non al tipo della variabile di riferimento.

Classi Astratte (abstract)

Una classe astratta è una classe contrassegnata con la parola chiave abstract che non può essere istanziata direttamente (non puoi fare new MyAbstractClass()). Serve come "modello parziale" per altre classi.

Metodi Astratti

Una classe astratta può contenere metodi astratti: metodi dichiarati senza corpo (senza parentesi graffe e senza codice), terminanti con un punto e virgola. Le sottoclassi non astratte sono obbligate ad implementare tutti i metodi astratti ereditati.

Code
abstract class Shape {
    String color;

    // Metodo astratto (senza corpo)
    public abstract double getArea();
}

class Circle extends Shape {
    double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    // Obbligatorio implementare getArea
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

Dynamic Binding / Late Binding

Come fa Java a sapere quale metodo eseguire a runtime? Questo processo è noto come Late Binding (o Dynamic Binding). A differenza dell'overloading (risolto a tempo di compilazione), il compilatore non sa quale metodo specifico verrà eseguito. Genera invece un'istruzione di chiamata generica. A runtime, la Java Virtual Machine (JVM) esamina l'oggetto reale in memoria e invoca l'implementazione del metodo definita nella classe di quell'oggetto.

Classi Astratte e Mancata Implementazione

Se una sottoclasse eredita da una classe astratta, ha due opzioni:

  1. Implementare tutti i metodi astratti: in questo caso la classe può essere una classe normale (concreta) ed essere istanziata.
  2. Dichiararsi astratta: se la sottoclasse non fornisce un'implementazione per tutti i metodi astratti ereditati, deve essere a sua volta dichiarata con la parola chiave abstract.
Code
abstract class Animal {
    public abstract void makeSound();
}

// Questa classe DEVE essere astratta perché non implementa makeSound()
abstract class Canine extends Animal {
    // Eredita makeSound() ma non lo implementa
}

Prova tu

Esercizio#java.m3.l2.e1
Tentativi: 0Caricamento…

Dichiara una variabile s di tipo Shape e assegnale un nuovo oggetto Circle usando il polimorfismo.

Caricamento editor…
Mostra suggerimento

Scrivi `Shape s = new Circle();` per usare il tipo base come tipo del riferimento.

Soluzione disponibile dopo 3 tentativi

Esercizio#java.m3.l2.e2
Tentativi: 0Caricamento…

Rendi la classe Shape astratta e aggiungi il metodo astratto double getArea(). Poi completa Square in modo che estenda Shape ed implementi getArea() ritornando side * side.

Caricamento editor…
Mostra suggerimento

Dichiara `public abstract double getArea();` in Shape. In Square, aggiungi `@Override public double getArea() { return side * side; }`.

Soluzione disponibile dopo 3 tentativi

Esercizio#java.m3.l2.e3
Tentativi: 0Caricamento…

Dichiara nel main un array di tipo Shape[] contenente un oggetto Circle con raggio 2.0 e un oggetto Square con lato 3.0. Successivamente, usa un ciclo (for o for-each) per stampare a console l'area di ciascuna forma invocando il metodo getArea().

Caricamento editor…
Mostra suggerimento

Dichiara l'array con `Shape[] shapes = { new Circle(2.0), new Square(3.0) };` e scorrilo con un ciclo `for (Shape s : shapes)` stampando `s.getArea()`.

Soluzione disponibile dopo 3 tentativi