EreditarietàL’ereditarietà è una delle caratteristiche più importanti che contraddistinguono la Programmazione Object Oriented (OOP) e senza alcun dubbio è anche la caratteristica più usata e “trasparente“.

Con questo articolo vorrei approfondire l’argomento per tutti quelli che si stanno avvicinando al linguaggio Java per la prima volta, ma le nozioni di cui vado a parlare sono valide per qualunque altro linguaggio che faccia uso degli oggetti. Questo significa che, a prescindere dal linguaggio utilizzato per programmare, in questo articolo verranno date nozioni generali sull’ereditarietà. Gli esempi saranno comunque scritti in Java, ma la filosofia non è vincolante.

Cominciamo con la definizione di ereditarietà: l’ereditarietà è la caratteristica per la quale un oggetto di classe B (detta classe derivata) che deriva da A (detta classe madre) possiede tutte le caratteristiche della classe madre.

Questa definizione sembra banale e riduttiva, ma non lo è: affermare che un oggetto B possiede tutte le caratteristiche di A, infatti, significa affermare che l’oggetto B è anche un oggetto di tipo A. Cerchiamo di spiegarlo con un esempio terra terra, che ci deriva dalla geometria piana. Tutti noi abbiamo in testa il concetto di Figura. Quando parliamo di Triangoli, Rettangoli e Cerchi tutti noi facciamo riferimento a delle figure. Questo ci suggerisce un legame tra questi concetti: un Triangolo è una Figura, un Rettangolo è una Figura, un Cerchio è una Figura. Tradotto in termini informatici, possiamo affermare che la classe Triangolo deriva dalla classe Figura, e via dicendo per le altre. Il termine tecnico (e inglese) con cui viene evidenziato questo legame è proprio “is-a” (cioè “è un”). Vediamo con del codice Java come si concretizza questo fatto:

public abstract class Figura {
   public abstract int calcolaArea();
   public void chiSono() { System.out.println("Sono una figura"); }
}

public class Triangolo extends Figura {
   private int base;
   private int altezza;

   public Triangolo() { this(3, 4); }
   public Triangolo(int base, int altezza) {
      this.base = base;
      this.altezza = altezza;
   }
   public int calcolaArea() { return (base * altezza) / 2; }
}

Tralasciamo per il momento il modificatore abstract che verrà approfondito in un prossimo articolo. Possiamo osservare che l’ereditarietà avviene tramite la parola chiave extends. Con questa “istruzione” andiamo a dire che la classe Triangolo estende per ereditarietà la classe Figura e, di conseguenza, ne eredita tutte le proprietà. Conseguentemente, la classe Triangolo possiede già, per costruzione, i due metodi calcolaArea() e chiSono() (perchè venga ridefinito, poi, il metodo calcolaArea() sarà argomento dello stesso articolo in cui si parlerà del modificatore abstract). Se richiamiamo, quindi, il metodo chiSono() su un oggetto di tipo Triangolo, non otterremo alcun errore e l’esecuzione produrrà la scritta a video “Sono una figura“. E questo risultato non può sorprenderci: un Triangolo, infatti, è una Figura.

Possiamo anche spingerci un po’ oltre e cercare di caratterizzare prima la figura Rettangolo e, subito dopo, specializzarla nella classe Quadrato: in questo modo un Rettangolo sarà una Figura e un Quadrato sarà un Rettangolo (un quadrato, infatti, è una forma particolare di rettangolo). Vediamolo con il codice:

public class Rettangolo extends Figura {
   private int base;
   private int altezza;

   public Rettangolo() { this(0, 0); }
   public Rettangolo(int base, int altezza) {
      this.base = base;
      this.altezza = altezza;
   }
   public int calcolaArea() { return (base * altezza); }
   public int getBase() { return base; }
   public int getAltezza() { return altezza; }
}

public class Quadrato extends Rettangolo {
   public Quadrato() { super(0, 0); }
   public Quadrato(int lato) { super(lato, lato); }
   public int getLato() { return getBase(); }
}

Cosa si può dire ora della classe Quadrato? Facile: si può dire che è un Rettangolo, visto che la estende come classe. Ma si può dire anche di più: un Quadrato è una Figura. Non solo concettualmente, ma anche nella pratica informatica: Quadrato è sulla stessa linea di derivazione di Figura (Figura è la classe “nonna”), quindi è anche una Figura. Con questo aggiungiamo un tassello alla teoria: un oggetto di classe X è anche oggetto di ogni classe della sua linea di derivazione.

Una cosa importante da tenere in considerazione è che in Java una classe può ereditare al massimo da una sola classe; in altri linguaggi, come il C++, questa “limitazione” non esiste. Per arginare questo problema, in Java è stato introdotto il concetto di interfaccia. Attraverso l’utilizzo delle interfacce una classe può assumere diversi comportamenti: ciò che accade, in buona sostanza, con l’ereditarietà multipla. Ecco, quindi, che una classe, assumendo i comportamenti di più interfacce, può essere vista come una particolare istanza di quelle interfacce. L’esempio più classico è quello dell’ActionListener: è possibile scrivere una classe qualunque che implementi ActionListener e, conseguentemente, è possibile passare qualunque classe al metodo addActionListener(): sia essa un pannello, una Finestra o una qualunque altra cosa, purchè essa implementi l’interfaccia ActionListener, diventando così un oggetto di tipo ActionListener.

Un’ultima cosa prima di concludere: tutte le classi Java hanno come classe madre (chiamata anche superclasse) all’origine la classe Object. Questo significa che automaticamente quando scriviamo una classe essa deriva (senza doverlo specificare) da Object. Con questo espediente è possibile, quindi, parlare genericamente di un qualsiasi Object per riferirsi ad un oggetto di classe qualunque.

E con questo vado a concludere l’argomento. Resta bene inteso che, per problemi di spazio, non tutte le peculiarità sono state trattate, ma rimandiamo ad eventuali commenti l’approfondimenti di eventuali aspetti non trattati. Buona Programmazione.

Tags: