Upcast e downcast delle classi in Java

package vale.io;

public class Prova {

public Prova() {
M m = new N();
//N n = new N();
//M m=n;
m.f1();
((N)m).f2(); //devo fare il casting altrimenti cerca in M se c’è f2()
}

public static void main(String[] args) {
new Prova();
}
}
class M {
void f1() {
System.out.println(“f1_di_M”);
}
}
class N extends M{
//se sovrascrivo la classe allora c’è dynamic binding
//altrimenti stampa “f1”
void f1() {
System.out.println(“f1_di_N”); }

void f2() {
System.out.println(“f2”);
}
}

Le seguenti espressioni si equivalgono:

M è superclasse di N
N è sottoclasse di M
N è figlia di M
N eredita da M

Se creo un oggetto (altrimenti detto un’istanza) di tipo M:
M m = new M();

Potrà accedere al suo metodo (altrimenti detto funzione) di nome f1
m.f1();

f1_di_M

package vale.io;

public class Prova {

public Prova() {
M m = new M();
m.f1();
}

public static void main(String[] args) {
new Prova();
}
}
class M {
void f1() {
System.out.println(“f1_di_M”);
}
}
class N extends M{

void f2() {
System.out.println(“f2”);
}
}

Se provo a includere nel codice sorgente l’istruzione:
m.f2();
In fase di compilazione ottengo il seguente errore
“Prova.java”: cannot find symbol; symbol : method f2(), location: class vale.io.M at line 10, column 7
(oviamente il numero di riga e colonna possono variare).
Il compilatore riferisce che non ha trovato il metodo f2() nella classe M.

Se invece creo un oggetto di tipo N:
N n = new N();

Potrà accedere ai suoi metodi inserendo le istruzioni:
n.f1();
n.f2();

package vale.io;

public class Prova {

public Prova() {
N n = new N();
m.f1();
m.f2();}

public static void main(String[] args) {
new Prova();
}
}
class M {
void f1() {
System.out.println(“f1_di_M”);
}
}
class N extends M{

void f2() {
System.out.println(“f2”);
}
}

E otterrà in stampa:

f1_di_M
f2

Questo succede perché nella classe N manca la funzione f1() quindi la stessa viene ricercata nella sua superclasse.

Ora voglio complicare un po’ la cosa. Creo un oggetto di tipo N “puntato” da un riferimento di tipo M.
M m = new N();
Oppure si può scrivere anche:
N n = new N();
M m=n;
Provo ad accedere al metodo f1 che è nella classe M
Ottenendo il codice:

package vale.io;

public class Prova {

public Prova() {
M m = new N();
//N n = new N();
//M m=n;
m.f1();
}

public static void main(String[] args) {
new Prova();
}
}
class M {
void f1() {
System.out.println(“f1_di_M”);
}
}
class N extends M{

void f2() {
System.out.println(“f2”);
}
}

Tutto funziona e viene stampato: f1_di_M
Come mai? Potrebbe sembrare che ciò non debba funzionare perché m è un oggetto di tipo N ed N ha solo il metodo f2().
Funziona perché il riferimento ha tipo M quindi cerca nella classe M e trova il suo metodo.

Provo ad eseguire il seguente codice con l’aggiunta di una funzione membro f1() anche nella classe N

package vale.io;

public class Prova {

public Prova() {
M m = new N();
m.f1();
}

public static void main(String[] args) {
new Prova();
}
}
class M {
void f1() {
System.out.println(“f1_di_M”);
}
}
class N extends M{
void f1() {
System.out.println(“f1_di_N”);

void f2() {
System.out.println(“f2”);
}
}

Ora invece che stamparmi: f1_di_M stampa f1_di_N.
Questo succede perché Java usa il dynamic binding ovvero cerca prima nella classe dell’oggetto istanziato trova il metodo chiamato poi cerca il metodo nella superclasse

Potrebbe sembrare allora che inserendo l’istruzione
m.f2();
Il codice sorgente compili.
“Prova.java”: cannot find symbol; symbol : method f2(), location: class vale.io.M at line 10, column 7
Il metodo f2() non è stato trovato nella classe M
Ciò non è vero perché m rimane sempre un riferimento di tipo M che accetta solo le firme dei metodi contenuti nella sua classe.

Però io so che m è riferimento a un oggetto della classe N. Quindi posso garantire al compilatore che, consapevole di quello che faccio, m si riferisce a un’istanza di N facendo un cast.:
((N)m).f2();

package vale.io;

public class Prova {

public Prova() {
M m = new N();
((N)m).f2(); //devo fare il casting altrimenti cerca in M se c’è f2()
}

public static void main(String[] args) {
new Prova();
}
}
class M {
void f1() {
System.out.println(“f1_di_M”);
}
}
class N extends M{
//se sovrascrivo la classe allora c’è dynamic binding
//se cancello questa funzione stampa “f1”
void f1() {
System.out.println(“f1_di_N”); }

void f2() {
System.out.println(“f2”);
}
}

L’output del programma è

f2

linux

Articolo precedente

Clam AntiVirus