Vous êtes sur la page 1sur 10

Ereditarieta in Java

Definire una sottoclasse

In Java presente solo unereditariet singola, cio una sottoclasse pu avere una sola superclasse. Per creare una sottoclasse si utilizza la parola chiave extends, secondo la seguente sintassi: class <nome classe> extends <nome superclasse> Per il resto, la sottoclasse mantiene la struttura di una classe normale.

Accedere ai membri della superclasse

Consideriamo la seguente relazione di generalizzazione/specializzazione:

Figura 1 - Relazione di ereditariet tra due classi

e la sua implementazione in Java, in base alle seguenti supposizioni: 1. la variabile di istanza numB pubblica; 2. i metodi setNum e getNum sono pubblici; 3. entrambe le classi hanno solo un costruttore di default; 4. la variabile di istanza numD privata (me se fosse pubblica non cambierebbe niente in questo esempio).

Bocchi Cinzia Ultimo aggiornamento: 20/11/2012

class ClasseBase { public int numB; public void setNum(int numB) { this.numB= numB; } public int getNum() { return numB; } } class ClasseDerivata extends ClasseBase { private int numD; } Creiamo una istanza di ClasseDerivata, che chiameremo d, utilizzando il costruttore di default: ClasseDerivata d = new ClasseDerivata(); Loggetto creato rappresentato in figura 2.

Figura 2 - Istanza della classe ClasseDerivata

I metodi di ClasseDerivata possono accedere:


alla variabile di istanza numB in scrittura, ad esempio assegnando ad essa un

valore

numB = 10;
alla variabile di istanza numB in lettura, per esempio

int numero = numB;


alla variabile di istanza numD in scrittura, ad esempio assegnando ad essa un

valore

numD = 100;

alla variabile di istanza numD in lettura, per esempio

int numero = numD; La variabile di istanza numB, definita all'interno di ClasseBase, si comporta a tutti gli effetti come se fosse una variabile dell'oggetto d, istanza di ClasseDerivata.

Bocchi Cinzia Ultimo aggiornamento: 20/11/2012

Sull'oggetto d inoltre possibile richiamare i metodi setNum e getNum, proprio come se fossero definiti all'interno di ClasseDerivata. Per esempio: d.setNum(5); assegna alla variabile di istanza numB dell'oggetto d il valore 5. int numero = d.getNum(); assegna alla variabile intera numero il valore dell'attributo numB dell'oggetto d. Supponiamo ora che la variabile di istanza numB non sia pubblica ma privata, come solitamente accade, e consideriamo nuovamente l'implementazione delle due classi. class ClasseBase { private int numB; public void setNum(int numB) { this.numB = numB; } public int getNum() { return numB; } } class ClasseDerivata extends ClasseBase { private int numD; } In questo caso, le modalit di accesso alla variabile di istanza numB sono diverse: i metodi definiti in ClasseDerivata non possono pi utilizzare numB come se si trattasse di una variabile di istanza della classe stessa, ma per accedere in lettura e scrittura devono utilizzare i metodi getNum e setNum:
l'accesso a numB in scrittura possibile solo nel seguente modo

this.setNum(10);
l'accesso a numB in lettura possibile solo nel seguente modo

int numero = this.getNum();

Istanziare una sottoclasse

Quando si crea un oggetto come istanza di una classe derivata, Java inserisce automaticamente la chiamata al costruttore della classe base, nel costruttore della classe derivata. Possiamo distinguere tre casi:

1. la classe base ha come unico costruttore il costruttore di default; il caso visto

in precedenza; 2. nella classe base definito un costruttore senza parametri; riconducibile al caso 1;
Bocchi Cinzia Ultimo aggiornamento: 20/11/2012

3. nella classe base definito un costruttore con almeno un parametro.


E bene ricordare che il costruttore di default disponibile solo se nella classe non sono definiti altri costruttori. Per analizzare il secondo caso, definiamo in ClasseBase e in ClasseDerivata rispettivamente i seguenti costruttori senza parametri: public ClasseBase() { System.out.println(costruttore della classe Base); } public ClasseDerivata() { System.out.println(costruttore della classe Derivata); } Quando creiamo loggetto d con listruzione ClasseDerivata d = new ClasseDerivata(); viene prodotto loutput: costruttore della classe Base costruttore della classe Derivata Ci conferma quanto detto in precedenza, ossia che il costruttore della classe derivata effettua implicitamente una chiamata al costruttore della classe base e, pertanto, la classe base viene inizializzata prima della classe derivata. Per analizzare il terzo caso introduciamo in ClasseBase un costruttore con un parametro: public ClasseBase(int numB) { this.numB = numB ; } Quando creiamo loggetto d con listruzione ClasseDerivata d = new ClasseDerivata(); il costruttore della classe derivata richiama implicitamente il costruttore della classe base, ma non in grado di passare ad esso il valore del parametro numB. In altre parole il metodo ClasseBase(int numB) non pu essere eseguito correttamente perch listruzione this.numB = numB genera un errore in fase di compilazione, non essendo definito il valore di numB. In questo caso, allora, il costruttore della superclasse immediatamente precedente nella gerarchia, deve essere richiamato esplicitamente utilizzando la parola chiave super, secondo la seguente sintassi: super(<lista di parametri>);

Bocchi Cinzia Ultimo aggiornamento: 20/11/2012

dove lista di parametri contiene i parametri attuali, separati da virgola, da passare al metodo costruttore della classe base. Possiamo, a questo punto, riscrivere il codice nellesempio, tenendo conto di quanto stato detto class ClasseBase { private int numB; public ClasseBase(int numB) { this.numB = numB; } public void setNum(int numB) { this.numB = numB; } public int getNum() { return numB; } } class ClasseDerivata extends ClasseBase { private int numD; public ClasseDerivata(int numB) { super(numB); } } Attenzione! La chiamata esplicita al costruttore di una classe base deve essere inserita come prima istruzione allinterno del costruttore della classe derivata (prima ancora della dichiarazione di eventuali variabili locali). Supponendo che il valore del parametro num sia 10, la creazione delloggetto d determina la seguente situazione: ClasseDerivata d = new ClasseDerivata(10); delle due classi considerate

Figura 3 - Istanza della classe ClasseDerivata

Se si desidera definire per la classe derivata un costruttore che si occupi di inizializzare anche la variabile di istanza numD, si pu aggiungere: public ClasseDerivata(int numB, int numD) { super(numB); this.numD = numD; }

Bocchi Cinzia Ultimo aggiornamento: 20/11/2012

La creazione dell'oggetto d, avviene con la chiamata: ClasseDerivata d = new ClasseDerivata(10, 5); la quale crea un'istanza di ClasseDerivata, inizializzando numB al valore 10 e numD al valore 5, come si pu vedere nel seguente diagramma.

Modificare linterfaccia della sottoclasse

Esistono due modi per modificare linterfaccia (cio l'insieme dei metodi pubblici) della classe derivata.

1. Possiamo

definire nella sottoclasse, qualche metodo in pi, se occorre (estensione dei metodi); ad esempio aggiungiamo alla classe derivata il metodo che calcola e restituisce la somma di numB e di numD class ClasseDerivata extends ClasseBase { private int numD; public ClasseDerivata(int nb,int nd) { super(nb); numD= nd; } public int somma() { int s; int nb; nb= this.getNum(); s= numD+nb; return s; } }

//restituisce numB in nb

Attenzione! Il metodo somma pu essere richiamato su un qualsiasi oggetto della classe ClasseDerivata, ma non su un oggetto della classe ClasseBase. Per esempio, se b un'istanza di ClasseBase, la chiamata int somma= b.somma(); genera un errore in fase di compilazione, perch somma non un metodo di ClasseBase.

Bocchi Cinzia Ultimo aggiornamento: 20/11/2012

2. Possiamo ridefinire nella sottoclasse uno o pi metodi della superclasse,


modificandone limplementazione ma mantenendo uguale l'intestazione (firma), ossia il nome del metodo, il numero e il tipo dei parametri (overriding dei metodi) class ClasseDerivata extends ClasseBase { private int numD; public ClasseDerivata(int nb,int nd) { super(nb); numD= nd; } public int somma() { int s; int nb; nb= this.getNum(); //restituisce numB in nb s= numD+nb; return s; } public void setNum(int num) { numD= num; } public int getNum() { return numD; } } I metodi ridefiniti differiscono da quelli della superclasse solo per il codice contenuto nel corpo del metodo, che assegna il valore del parametro alla variabile numD, anzich a numB, o analogamente restituisce il valore della variabile numD, invece di numB.

Binding dinamico e statico


Quando in una sottoclasse sono ridefiniti alcuni metodi della superclasse, come fa il compilatore a sapere quale metodo chiamare? Per esempio:
d.setNum(4);

determiner la chiamata del metodo setNum della classe base o della classe derivata? Il problema non pu essere risolto in fase di compilazione ma a tempo di esecuzione con il binding dinamico: l'interprete individua il tipo (la classe) dell'oggetto sul quale viene chiamato il metodo;

Bocchi Cinzia Ultimo aggiornamento: 20/11/2012

in seguito ricerca nella classe e in tutte le sue superclassi i metodi non privati che hanno lo stesso nome; infine esclude tutti i metodi che hanno lo stesso nome ma numero o tipo di parametri diversi.

Se l'interprete individua pi metodi coincidenti in nome, numero e tipo di parametri, sceglie quello che si trova nella classe al livello pi basso della gerarchia di ereditariet. Nel caso dell'esempio, il compilatore sceglie il metodo setNum della classe derivata. Se il metodo chiamato private, static o final, il compilatore sa che pu utilizzare solo un metodo della classe di cui l'oggetto rappresenta un'istanza: in tal caso il problema viene risolto in fase di compilazione e si parla di binding statico. Nota: Vedremo pi avanti che il tipo di un oggetto non coincide necessariamente con il tipo della variabile che contiene il riferimento ad esso. Il tipo di un oggetto sempre e solo la classe di cui loggetto rappresenta unistanza.

Chiamare metodi della superclasse


La parola chiave super pu essere utilizzata anche nel caso in cui sia necessario richiamare un metodo della superclasse, ridefinito nella sottoclasse con il meccanismo di overriding. Ad esempio, come constatato in precedenza, se richiamiamo il metodo setNum utilizzando listruzione d.setNum(4); viene eseguito il metodo setNum della classe derivata e non quello della classe base; pertanto il valore 4, passato come parametro al metodo, sar assegnato alla variabile di istanza numD. Linterprete Java, infatti, cerca nella classe di cui oggD un istanza un metodo compatibile con quello invocato; se lo trova lo esegue, altrimenti lo ricerca nella classe che si trova al livello immediatamente superiore, e cos via fino a percorrere eventualmente tutta la gerarchia di classi verso lalto. Se vogliamo richiamare il metodo setNum della classe Base dobbiamo usare la parola chiave super, secondo la seguente sintassi super.<nome del metodo della superclasse>; In questo caso super.setNum(4); assegner il valore 4 alla variabile di istanza numB.

Bocchi Cinzia Ultimo aggiornamento: 20/11/2012

Polimorfismo
Un oggetto della sottoclasse anche un oggetto della superclasse, ma non vale il viceversa. In altre parole, per creare unistanza di Classe Derivata possibile scrivere sia ClasseDerivata d = new ClasseDerivata (0,10); sia ClasseBase d = new ClasseDerivata (0,10); In entrambi i casi le istanze create sono dello stesso tipo, cio ClasseDerivata. Si dice che il tipo run time delloggetto d ClasseDerivata. Il tipo della variabile reference b invece diverso: ClasseDerivata nel primo caso, ClasseBase nel secondo. Questa caratteristica, tipica dei linguaggi a oggetti, prende il nome di polimorfismo (molte forme). Allatto dellinvocazione di un metodo su d, linterprete individuer il tipo run time (binding dinamico) e sar in grado di selezionare correttamente il metodo da eseguire. Tutto ci realizzabile grazie al principio di sostituzione di Liskov, in base al quale sempre possibile sostituire un oggetto della superclasse con un oggetto della sottoclasse. La seguente porzione di codice non viene invece accettata dal compilatore: ClasseDerivata d = new ClasseBase (0); Non mai possibile sostituire un oggetto della sottoclasse con un oggetto della superclasse.

Impedire l'ereditariet - classi e metodi final


Per impedire che una classe possa essere estesa, sufficiente dichiararla final. Esempio final class ClasseFinal { . } La seguente dichiarazione: class Sottoclasse extends ClasseFinal genera un errore in fase di compilazione. Tutti i metodi dichiarati in una classe final sono a loro volta final; fanno eccezione le variabili di istanza.

Bocchi Cinzia Ultimo aggiornamento: 20/11/2012

Se non si vuole impedire l'estensione dell'intera classe ma solo di alcuni metodi, basta dichiararli final: in questo caso sar possibile derivare una sottoclasse dalla classe di base, ma i metodi final non saranno ereditati.

____________________________________________________________________
Quest'opera stata rilasciata con licenza Creative Commons Attribution-ShareAlike 3.0 Unported. Per leggere una copia della licenza visita il sito web http://creativecommons.org/licenses/by-sa/3.0/ o spedisci una lettera a Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.

Bocchi Cinzia Ultimo aggiornamento: 20/11/2012

10