Ereditarietà come meccanismo di astrazione

L'esempio delle classi Persona, Studente e Professore mostra come usando l'ereditarietà si possono fattorizzare informazioni comuni a più classi.
Le informazioni comuni a Studente e Professore sono state fattorizzate nella superclasse Persona, dalla quale entrambe ereditano.




Verso le Classi Astratte...

Le classi astratte consentono di usare questo meccanismo di "fattorizzazione" delle informazioni comuni, anche quando la superclasse risultante non sarebbe "ben definita".

Supponiamo di avere le classi Sfera e Cubo:
 

public class Sfera { 
    double raggio; 
    double pesoSpecifico; 
    public Sfera(double raggio, double ps) { 
        this.raggio = raggio; 
        pesoSpecifico = ps; 
    }  
    public double volume() { return 4/3 * Math.PI * Math.pow(raggio,3); } 
    public double superficie() { return 4 * Math.PI * raggio * raggio;} 
    public double peso() { return pesoSpecifico * volume(); } 
} 


public class Cubo {      double lato;      double pesoSpecifico;      public Cubo(double lato, double ps) {          this.lato = lato;         pesoSpecifico = ps;      }      public double volume() { return Math.pow(lato,3); }      public double superficie() { return 6*lato*lato; }      public double peso() { return pesoSpecifico * volume(); }  }




Sfera e Cubo come Solido

Anche qui troviamo ridondanza. Vorremmo definire una classe Solido che fattorizzi le informazioni comuni. Solido dovrebbe contenere:
  • la variabile pesoSpecifico;
  • il metodo peso(), identico nelle due classi;
  • i metodi volume() e superficie(), per due motivi:
    • ogni solido ha un "volume" e una "superficie"!
    • il metodo peso() invoca volume()...
Ma purtroppo non c'è un modo default per calcolare superficie e volume di un solido generico! Come possiamo implementarlo?

Soluzione: Definire  superficie() volume() come metodi astratti, aventi solo l'intestazione ma non l'implementazione. Di conseguenza Solido è una classe astratta.
 




Classi Astratte

public abstract class Solido { 
    double pesoSpecifico; 
    public Solido(double ps){pesoSpecifico = ps;}
    public double peso (){ return volume() * pesoSpecifico; } 
    public abstract double volume(); // metodo astratto 
    public abstract double superficie(); // metodo astratto 
}

Alcune cose importanti da ricordare:

  • Una classe astratta viene definita premettendo il modificatore abstract.
  • Non può essere istanziata, ma può avere costruttori.
  • Può contenere metodi astratti, cioè con modificatore abstract e senza corpo.
  • Metodi astratti possono essere dichiarati SOLO in classi astratte.
  • Se una classe concreta eredita da una classe astratta, deve fornire una implementazione per tutti i metodi astratti ereditati.
  • Classi astratte possono essere superclassi o sottoclassi di altre classi, sia astratte che non.
Vediamo la nuova definizione di Sfera e Cubo.
 
public class Sfera extends Solido { 
    double raggio; 
    // pesoSpecifico e'  definito nella superclasse 
    public Sfera(double raggio, double ps) { 
        super(ps);
        this.raggio = raggio; 
    }  
    public double volume() { return 4/3 * Math.PI * Math.pow(raggio,3); } 
    public double superficie() { return 4 * Math.PI * raggio * raggio;} 
    // peso() e' definito nella superclasse 
    public String toString(){ return "Sfera ("+ raggio +")"; }
} 


public class Cubo extends Solido {      double lato;      // pesoSpecifico e'  definito nella superclasse      public Cubo(double lato, double ps) {          super(ps);         this.lato = lato;     }      public double volume() { return Math.pow(lato,3); }      public double superficie() { return 6*lato*lato; }      // peso() e' definito nella superclasse      public String toString(){ return "Cubo ["+ lato +"]"; } } 




Esempi di classi astratte

  • Cubo, Sfera, Solido e UsaSolido

    public class UsaSolido {
        public static void main(String args[]) {
            Solido sol;
    
            char scelta = ' ';
            do { System.out.print("Vuoi una sfera o un cubo? (s/c) > ");
                 scelta = Input.readChar();
            } while (scelta!='s' && scelta!='c');
            System.out.print("Peso Specifico? ");
            double ps = Input.readDouble();
            // Solido s = new Solido(ps);
            if (scelta == 's') {
                System.out.print("Raggio? ");
                double raggio = Input.readDouble();
                sol = new Sfera(raggio, ps);
            } else {
                System.out.print("Lato? ");
                double lato = Input.readDouble();
                sol = new Cubo(lato, ps);
            }
            System.out.println( "Ho creato un solido \"" + sol.toString() +
                                "\" con volume " + sol.volume() + 
                                " e peso " + sol.peso() );
        }
    } 
    

  •  
  • Rettangolo, Triangolo, Figura e UsaFigura.