L’EREDITARIETA’ IN JAVA
OVERRIDE DEI METODI
In questo post parleremo di uno degli argomenti più importanti che stanno alla base dell’ereditarietà in Java. Nelle lezioni precedenti abbiamo introdotto in concetto di overload, vediamo una figura.
Sappiamo che possiamo definire metodi aventi lo stesso nome a patto che il numero e il tipo di parametri sia differente (diverse signature Overload). Vedremo adesso con l’ereditarietà cosa succede se dichiariamo nella sottoclasse un metodo avente stessa signature (Override). Vediamo la figura.
Quello che abbiamo fatto si chiama Override dei metodi.
L’override di un metodo avviene quando in una sottoclasse andiamo a ridefinire un metodo presente nella superclasse, a patto che questo metodo abbia lo stesso nome, stesso tipo di ritorno e stessa signature (cioè lo stesso numero e lo stesso tipo di parametri). Creiamo la classe di test.
Come vedi viene eseguito il metodo ridefinito in BClass. Dobbiamo fare questo quando in una sottoclasse vogliamo ridefinire in maniera integrale il comportamento di un metodo che si trova nella superclasse. Ora non sempre vogliamo questo ma il più delle volte vogliamo estendere il comportamento di un metodo nella superclasse, usando l’override e ampliandone le funzionalità. Allora richiamiamo con la keyword super il metodo della superclasse per avere il comportamento standard, poi specializziamo la sottoclasse. La figura chiarisce i concetti esposti, prima richiamiamo il comportamento standard del metodo invocandolo con super sulla superclasse, poi lo specializziamo nella sottoclasse.
Facciamo attenzione a una cosa.
In questo caso non abbiamo ottenuto l’override dei metodi piuttosto l’overload in quanto nella sottoclasse in myMethod è diverso il numero di parametri. Possiamo usare indifferentemente sia l’una che l’altra versione, in base alle nostre esigenze.
OVERRIDE E POLIMORFISMO
In questa sezione andiamo a vedere perché l’override è così importante nella OOP. Una caratteristica molto importante dell’override è il Dynamic Method Dispatch, detto in parole povere ciò significa che Java è in grado di eseguire un metodo in override scegliendolo a runtime e non a tempo di compilazione e questo porta come conseguenza che è possibile implementare un’altra forma di polimorfismo. Vediamo un esempio.
Abbiamo tre metodi in override ciascuno con un comportamento differente.
Andiamo a creare un Array di oggetti AClass. Adesso abbiamo una variabile arr che è di tipo AClass, ma che in realtà contiene un Array che punta ad oggetti di livello inferiore. Vediamo la figura.
Nel ciclo foreach avviene la selezione dinamica dei tipi a runtime, in presenza di override vengono scelti gli oggetti riferiti da una variabile e non quelli presenti nella classe di definizione. La figura riassume quello che succede. Questa è la più importante forma di polimorfismo presenta in Java e se ben gestita consente di realizzare programmi molto sofisticati.
METODI ASTRATTI E CLASSI ASTRATTE
Riprendiamo l’esempio precedente. Vedi figura.
Potremmo volere definire un comportamento più generale nella superclasse, in particolare potremmo definire un metodo che di per sé non ha alcun comportamento, è come una sorta di segnaposto che obbliga a chi estende la classe ad implementarlo. Per fare tutto ciò si usa la keyword abstract.
Una classe può anche mischiare le due cose, cioè può avere alcuni metodi che sono astratti e altri no. Tuttavia, vi è una regola da seguire, se una classe contiene anche un solo metodo astratto tutta la classe diventa abstract.
Una classe astratta non può essere istanziata. Se una classe non intende ridefinire i metodi astratti della superclasse allora anche lei deve essere dichiarata abstract.
B2Class può essere istanziata e ridefinire il metodo.
LA KEYWORD FINAL
final viene utilizzata per impedire l’override dei metodi, la specializzazione di una classe e anche per creare delle costanti. Riprendiamo un esempio già visto.
In alcune situazioni vogliamo che la definizione di un metodo resti tale impedendone la ridefinizione e l’override nelle classi derivate.
Possiamo anche definire la classe final e impedirne l’ereditarietà.
final infine può essere usata per trasformare qualsiasi variabile in una costante.
final double PIGRECO = 3.14169
LA CLASSE OBJECT
Object è la classe base di tutte le classi in Java, sia quelle predefinite del linguaggio, sia quelle che creiamo noi. Quindi in cima a qualsiasi catena di ereditarietà c’è la classe Object. Quando dichiariamo una classe senza keyword extends, senza saperlo stiamo derivando da Object. Esso contiene tutta una serie di metodi, alcuni final quindi devono essere usati così come sono, mentre altri possono essere ridefiniti. Porremo l’attenzione su due di questi metodi: equals e toString.
Potremmo scrivere AClass anche in questo modo perché come abbiamo detto in cima alla catena di ereditarietà vi è la classe Object.
Il metodo equals è un metodo di istanza della classe Object e ha come parametro un tipo Object (una classe qualunque).
boolean equals(Object obj);
Ritorna false perché i due oggetti sono chiaramente diversi, infatti non hanno lo stesso reference. Però se assegniamo a=b questa volta il risultato è true perché i due oggetti hanno lo stesso reference. Puntano la stessa area di memoria. Vediamo toString();
Il metodo ritorna una versione visualizzabile di un oggetto. Ritorna il nome qualificato del Data Type dell’istanza AClass seguito dal simbolo @ e un numero esadecimale che rappresenta lo HashCode() dell’oggetto, che è un numero univoco che identifica l’oggetto stesso.
LINK AI POST PRECEDENTI
LINK AL CODICE SU GITHUB
ESECUZIONE DEL CODICE DI ESEMPIO
- Scaricare il codice da GITHUB, lanciare il file JAR con il seguente comando in Visual Studio Code, posizionandosi nella directory contenente il JAR.
java -jar –enable-preview CorsoJava.jar
- Oppure mettere in esecuzione il main che si trova nel file CorsoJava.java.
Scrivi un commento