Corso di Java

Ereditarietà e gerarchie di classi III

 

Se proviamo nuovamente a compilare il sorgente della classe “UomoDAffari”, otterremo il seguente errore:



che non ci dovrebbe stupire più di tanto: l’attributo nome dichiarato all’interno della classe “Uomo” è ad accesso privato, il che vuol dire, come abbiamo visto in precedenza, che nessuna classe esterna la può utilizzare direttamente.
Potremmo pensare di dichiararla ad accesso pubblico o di omettere il modificatore di accesso.
Queste soluzioni funzionano ma non sono esattamente quello che vorremmo, perché è nostra intenzione proteggere questo attributo da azioni esterne indesiderate.
La soluzione adeguata per questo problema è il modificatore di accesso protected, che ha pressappoco la stessa funzione di private, ma permette alle classi derivate di accedere a quell’attributo: proprio quello che cercavamo!

Se modifichiamo il codice della classe “Uomo”, sostituendo al modificatore private nella dichiarazione della variabile nome il modificatore protected, e proviamo a ricompilare, non sarà più segnalato alcun errore.
All’interno del corpo della classe “UomoDAffari” è stato definito un metodo già presente nella classe “Uomo”, ossia:

public void dice(String parole) {
        System.out.println(titoloDiStudio + ' ' + nome + ": - " + parole);
    }


Questa operazione prende il nome di ridefinizione di un metodo e serve per specializzare il comportamento di una classe figlio rispetto alla classe padre.
Infatti, quando viene invocato un metodo su una classe, l’interprete Java cerca la dichiarazione del metodo dapprima nel corpo della classe, se la trova la esegue, altrimenti risale la gerarchia di figlio in padre, finchè non trova una dichiarazione del metodo (ovviamente se la ricerca è infruttuosa viene rilevato un errore).
Tutto ciò è possibile grazie al fatto che ogni classe può avere al massimo una sola superclasse, per cui non esiste ambiguità nella ricerca del metodo.

In questo Java differisce da C++, nel quale è permessa l’ereditarietà multipla, cioè la possibilità di derivare attributi e metodi da più superclassi: sebbene rappresenti una buona possibilità di programmazione, l’ereditarietà multipla può portare a situazioni di ambiguità.
Supponiamo di definire una classe “GiocatoreDiGolf” nel seguente modo:

class GiocatoreDiGolf {
    ...
    public void dice(String parole) {}
    ...
}


Se fosse possibile ereditare da più classi, potremmo decidere di far derivare “UomoDAffariRicco” sia da “Uomo” che da “GiocatoreDiGolf” e di non ridefinire il metodo dice.
Nel momento in cui invocassimo tale metodo su un oggetto di tipo “UomoDAffariRicco”, non potremmo sapere con certezza se venisse eseguito il metodo definito nella classe “Uomo” o quello definito nella classe “GiocatoreDiGolf”.

Per ovviare a casi di ambiguità di tale tipo Java permette che una classe erediti solo da un’altra classe.
Questa soluzione elimina ogni causa di ambiguità al riguardo, ma si rivela troppo limitativa per sviluppare gerarchie di una certa complessità, che sono utili in gran parte delle applicazioni più comuni.
Per ovviare a un limite troppo stretto, Java permette di utilizzare le interfacce, ossia delle classi i cui metodi non eseguono alcuna istruzione.
Lavorare con le interfacce è come lavorare con le classi, senonché non è possibile creare direttamente un’istanza di un’interfaccia mediante l’istruzione new.
E’ possibile sia utilizzare interfacce nelle nostre classi, che definirne di nuove.
Per poter utilizzare un’interfaccia occorre utilizzare la parola chiave implements come parte della definizione di classe, ad es.

class UomoDAffari extends Uomo implements unaInterfaccia {
    ...
}

In questo esempio la classe “UomoDAffari” eredita gli attributi e i metodi della classe “Uomo” e quelli dell’interfaccia “unaInterfaccia”.
Poiché le interfacce forniscono solo le definizioni dei metodi, occorre implementare tali metodi nella propria classe, eliminando così la possibilità di casi ambigui, propria dell’ereditarietà multipla.
Inoltre non c’è limite al numero di interfacce che si possono utilizzare, ma esiste l’obbligo di implementare tutti i metodi di ognuna di esse.
L’argomento delle interfacce può essere compreso appieno dopo aver acquisito una maggiore esperienza con la programmazione orientata agli oggetti di Java, per cui ne rimandiamo la trattazione completa alle lezioni successive.

 

Torna all'indice Generale del corso di Corso di Java di Software Planet