Ciao,
iniziamo con un'affermazione che eredito per comodità dal "De Sio, Cesari"; non è una legge scritta in quanto è una pura classificazione fatta per comodità, non "ufficiale".
il polimorfismo si concretizza sotto 2 forme: polimorfismo per dati e polimorfismo per metodi.
il polimorfismo per dati lo si puo trovare nei cosiddetti parametri polimorfi (Padre p = new Figlio()).
il polimorfismo per metodi è suddiviso in overload ed override.
L'overload implica la capacità in fase di compilazione (perciò detta statica) di definire metodi omonimi purché non abbiano la stessa firma, quindi generalmente parliamo di metodi che si differenziano per parametri (tipo, numero o ordine).
L'override implica la possibilità di ridefinire a runtime (perciò detta dinamica) un metodo già presente in una classe padre, all'interno di una classe figlia.
Fatta questa basilare distinzione immagino sia chiaro che overload è polimorfismo puro!
Parliamo dei parametri polimorfici ed il binding dinamico.
Prendiamo in esame il codice:
public class Padre {
public void identificati() {
System.out.println("sono un Padre");
}
public void salutaDaPadre() {
System.out.println("saluto come un Padre");
}
}
public class Figlio extends Padre{
@Override
public void identificati() {
System.out.println("sono un Figlio");
}
public void salutaDaFiglio() {
System.out.println("saluto come un Figlio");
}
}
Abbiamo una classe Padre che implementa un metodo con una stampa a video ed una classe Figlio che effettua l'override del suddetto metodo per poterlo personalizzare. Inoltre abbiamo un altro metodo nella classe Padre (sempre pubblico e pertanto sempre ereditato ma stavolta non in override) ed un metodo proprio solo della classe Figlio (non richiamabile dalla classe Padre).
Poniamo il caso di avere il seguente caso d'uso(per esempio quindi nel nostro metodo main scriviamo il seguente codice):
Padre[] padri = new Padre[2];
padri[0] = new Padre();
padri[1] = new Figlio();
for(Padre p : padri) {
p.identificati();
p.salutaDaPadre();
// p.salutaDaFiglio();
}
Il precedente codice mostra una riga commentata che darebbe errore di compilazione senza commento. Questo perché il metodo salutaDaFiglio non è utilizzabile su un identificatore di tipo Padre: tale metodo è visibile solo a runtime, in fase di compilazione un record di tipo Padre non dichiara alcun metodo con quel nome; a runtime invece (quindi l'istanza cui punta quell'identificatore) lo dichiara.
Il binding dinamico lo si vede alla prima riga interna al for migliorato: la stampa prodotta da quell'istruzione verrà valutata a runtime e verrà quindi presa la versione del metodo relativa al tipo dell'istanza (Padre o Figlio che sia) e non più relativa al tipo dell'identificatore, che è sempre Padre in quel for.