ereditarieta

oggetti che ampliano o specializzano oggetti preesistenti

Le persone, gli studenti e i professori

Una realtà che ci è familiare è quella scolastica. Vogliamo rappresentare le persone che hanno a che fare con la scuola tramite la classe Persona. Queste persone possono essere i commessi del bar, il personale negli uffici, quelli che fanno il servizio al corridoio, il dirigente e così via. Di queste persone ci interessa il nome, il cognome, il ruolo svolto, lo stipendio mensile, la categoria (0: indefinito, 1 dirigente, 2: docente, 3: tecnico, 4: amministrativo, 5: ausiliario). La classe, con relativi costruttori, sarà una cosa del tipo:

public class Persona {
   private String nome, cognome;
   private String ruolo;
   private float stipendio;
   private byte categoria;

   public Persona() {
      nome = cognome = "<indefinito>";
      ruolo = "<non assegnato>";
      stipendio = 0.0f;
      categoria = 0;
   }

   public Persona(String n, String c, String r, float s, byte cat) {
      nome = n;
      cognome = c;
      ruolo = r;
      stipendio = s;
      categoria = cat;
   }
}

Prendiamo in considerazione ora gli alunni: senza dubbio sono persone. Sono persone un po' particolari perché oltre le caratteristiche comuni a tutte le persone, questi frequentano una classe, possono frequentare le lezioni di religione oppure di attività alternativa, quindi possiamo individuare la nuova proprietà che è la classe frequentata ed una ulteriore che è la situazione in merito alla religione. Nei linguaggi ad oggetti possiamo indicare il fatto che gli studenti sono persone con particolari caratteristiche tramite il concetto di ereditarietà: la classe Studente eredita tutto ciò che è nella classe Persona ed in più ci saranno le proprietà sopra dette che la persona generica non ha. In altre parole la classe Studente estende la classe Persona. Esplicitiamo tutti questi concetti tramite il seguente:

public class Studente extends Persona {
   private String nome, cognome;
   private String ruolo;
   private float stipendio;
   private boolean categoria;
   private String classe;
   private String religione;

   public Studente() {
      nome = cognome = "<indefinito>";
      ruolo = "<non assegnato>";
      stipendio = 0.0f;
      categoria = 0;
      classe = "<non assegnata>";
      religione = "lt;indefinito>";
   }

   public Studente(String n, String c, String r, String cl, String rel) {
      nome = n;
      cognome = c;
      ruolo = r;
      stipendio = 0.0f;
      this.categoria = 0;
      classe = cl;
      religione = rel;
   }
}

Nel modo appena proposto non si evidenziano vantaggi dal dire che la classe Studente è una classe che estende Persona, infatti abbiamo ricreato tutte le proprietà indistintamente nuove e vecchie. Dato però che il realtà uno studente è anche una persona non è necessario dire che ha un nome, questo è già presente in quanto persona, stessa cosa per tutte le proprietà della classe Persona. Allora - ed è anche più logico - le uniche proprietà che andranno dichiarate sono le nuove:

In sintesi

Quando quello che vogliamo fare non è creare un oggetto nuovo ma aggiungere/modificare delle funzionalità di un oggetto esistente possiamo farlo usando la parola chiave extends nella dichiarazione della classe seguita dal nome della classe che si vuole ampliare/cambiare. In questo caso l'oggetto definito avrà tutte le proprietà e tutti i comportamenti di quello originale con le aggiunte o le modifiche indicate nella classe.

Java è un linguaggio ad ereditarietà singola cioè si può estendere soltanto una classe alla volta.
public class Studente extends Persona {
   private String classe;
   private String religione;

   public Studente() {
      super();	// Per la spiegazione di super() guarda il paragrafo che segue
      classe = "<non assegnata>";
      religione = "lt;indefinito>";
   }

   public Studente(String n, String c, String r, String cl, String rel) {
      super(n, c, r, 0.0f, 0);
      classe = cl;
      religione = rel;
   }
}

La parola super sta ad indicare la classe dalla quale si è fatta l'estensione, si chiama infatti superclasse o classe padre. La classe che estende la superclasse si chiama classe estesa o classe figlia. Nel nostro esempio la superclasse è Persona, la classe figlia è Studente. L'invocazione di super() come metodo sta ad indicare il costruttore, in effetti si fa riferimento al nome della classe con le parentesi dei metodi...
Si utilizza super() in modo tale da invocare il costruttore per le proprietà definite in precedenza, lasciando qui il solo lavoro di dare un valore alle ultime definite. Si osserva che ci sono 2 invocazioni di super(): una senza argomenti (per chiamare il primo costruttore di Persona, quello senza argomenti) e l'altra con tanti argomenti (per chiamare il secondo costruttore di persona, quello con 5 argomenti).

Se provassimo però il codice sopra otterremmo un errore: abbiamo detto infatti che la parola private indica che la proprietà (o il metodo) non può essere visto al di fuori della classe. La classe derivata è a tutti gli effetti un'altra classe e quindi nulla traspare. La soluzione viene nella parola chiave protected: sta ad indicare che quanto segue (metodo o proprietà) è visibile solo a tutti i metodi della classe stessa (ovviamente) e a tutte le classi che estendono questa. Nel momento in cui andremo a dichiarare oggetti, dovremo porre attenzione su cosa dovrà essere pubblico, cosa privato e cosa protetto. La classe persona a questo punto sarà:

public class Persona {
   protected String nome, cognome;
   protected String ruolo;
   protected float stipendio;
   protected boolean categoria;

   public Persona() {
      nome = cognome = "<indefinito>";
      ruolo = "<non assegnato>";
      stipendio = 0.0f;
      categoria = 0;
   }

   public Persona(String n, String c, String r, float s, byte cat) {
      nome = n;
      cognome = c;
      ruolo = r;
      stipendio = s;
      categoria = cat;
   }
}

Allo stesso modo si potrà realizzare la classe Professore estensione di Persona.

public class Professore extends Persona {
   private String materia;
   private int orePotenziamento;

   public Professore() {
      super();
      materia = "<non assegnata>";
      orePotenziamento = 0;
   }

   public Studente(String n, String c, String r, String mat, String orePot) {
      super(n, c, r, 1000.0f, 2);
      materia = mat;
      orePotenziamento = orePot;
   }
}

Risparmio vincolato pluriennale

Riprendiamo l'esempio del risparmio vincolato. In quel caso la somma che si otteneva era data alla fine del periodo di investimento. Ipotizziamo però che riusciamo a trattenerci dallo spendere quei soldi e li manteniamo per un certo numero di anni. Quale sarà la somma totale?
Di certo ci appoggeremo al codice già scritto (ci dobbiamo ricordare di sostituire private interessiPercentualecon protected interessiPercentualesulle proprietà) e lo estenderemo con qualcosa in più:

public class RisparmioVincolatoPluriennale extends RisparmioVincolato {
   private byte durataPeriodo;
   private short numeroPeriodi;

   public RisparmioVincolatoPluriennale() {
      super();
      durataPeriodo = 0; //In mesi quanto dura il vincolo. Si consente solo 3 mesi, 6 mesi, 1 anno, 2 anni
      numeroPeriodi = 0;
   }

   public RisparmioVincolatoPluriennale(String n, String c, int cod, float euro, short a, byte m, byte g, float i, byte durata, short periodi) {
      super(n, c, cod, euro, a, m, g, i);
      if(durata==3 || durata==6 || durata==12 || durata==24) {
         durataPeriodo = durata;
      } else {
         durataPeriodo = 0;
      }
      if(numeroPeriodo > 0) {
         numeroPeriodi = periodi;
      } else {
         numeroPeriodi = 0;
      }
   }

   double fornisciCapitale() {
      if(durataPeriodo==0 || numeroPeriodi==0) {
         return 0;
      }
      /* La percentuale di interessi che viene pubblicizzata è riferita ad un periodo di un anno.
       * Se per esempio investiamo per 6 mesi al 2% in realtà (dato che è mezzo anno) avremo gli interessi dell'1% a fine periodo
       * Se per esempio investiamo per 24 anni al 3% in realtà (dato che sono 2 anni) avremo gli interessi del 6% a fine periodo
       */
      double interessiVeri = interessiPercentuale*(durata/12.0);
      // Invochiamo il metodo della superclasse che calcola il nuovo capitale a fine periodo, per il numero di periodi indicato
      double capitaleIniziale = importo;
      for(int i=0; i<numeroPeriodi; i++) {
         importo = fornisciCapitale();	//viene invocato il metodo della superclasse aggiornando il capitale sul quale calcola
      }
      double capitale = importo;
      importo = capitaleIniziale;	// si rimette l'importo al valore iniziale, come se nulla fosse cambiato
      return capitale;
   }
}

file it/esempi/Secondo.java

package it.esempio;

public class Secondo extends Primo {
	String c;
	public Secondo(int k, boolean p, String u){
		x=k;
		y=p;
		c=u;
	}
	public String descrivi(){
		return c+x+c;
	}
}

L'oggetto Secondo ha un metodo leggiVal()?

no si perché lo eredita da Primo

Considera il seguente frammento di programma inserito in un'altra classe:
Secondo o;
o = new Secondo(3,false,"#");
String m=o.descrivi();

Quanto vale m?

non si sa 3 n:3 attenzione: descrivi() viene ridefinito in Secondo #3# perché viene usato il metodo definito in Secondo