Implementare il polimorfismo

Il polimorfismo è la capacità di un interfaccia di assumere diverse forme, in java ci permette di poter passare diversi tipi di oggetti ad un metodo o classe, dimostriamolo con un semplice esempio :

public interface Aereo { public void vola(); }

public class Md80 implements Aereo {
  public void vola() { System.out.println("md80 sta volando"); }
}
public class Boeing747 implements Aereo {
  public void vola() { System.out.println("Boeing 747 sta volando"); }
}

public class TorreDiControllo {
  public void autorizzaDecollo(Aereo aereo) {
    aereo.vola();
  }
  public static void main(String... args) {
    TorreDiControllo c = new TorreDiControllo();
    c.autorizzaDecollo(new Md80());
    c.autorizzaDecollo(new Boeing747());
  }
}

Se eseguiamo questo codice avremmo in output

Md80 sta volando
Boeing 747 sta volando

Il metodo autorizzaDecollo di TorreDiControllo è definito per poter accettare tutti gli oggetti che implementano l’interfaccia Aereo, questo ci permetterà di utilizzare questo metodo anche se in futuro verranno creati nuovi tipi di aereoplano.

Come ricorderemo, Java ci permette di utilizzare un riferimento ad una superclasse senza dover dichiarare in cast esplicito, di fatto :

Boeing747 boeing = new Boeing747();
Aereo aereo = boeing;

La nostra variabile aereo punta a boeing747, non è necessario il cast esplicito perchè Aereo è una superclasse di Boeing747, la nostra variabile aereo avrà visibilità solo sui metodi definiti dall’interfaccia, e non a quelli propri di Boeing747, anche se dobbiamo tenere sempre a mente che aereo è un riferimento ad un oggetto creato in memoria, nel nostro caso, l’oggetto in memoria sarà sempre del tipo Boeing747, per cui, possiamo tornare all’oggetto originario mediante un cast esplicito :

Boeing747 boeing = new Boeing747();
Aereo aereo = boeing;
Boeing747 boeing2 = (Boeing747)aereo;

In questo caso tutto funziona correttamente, ma vediamo un esempio dove il cast non andrà a buon fine :

Boeing746 boeing = new Boeing747();
Aereo aereo = boeing;
Md80 md80 = (Md80)aereo; //FAIL

Anche se il compilatore non ci da errore, otterremo un ClassCastException, perchè l’oggetto in memoria è Boeing747 e noi stiamo cercando di fargli assumere la forma di Md80, che sono due oggetti diversi, ma perchè il compilatore non ci da errore ? perchè sia Boeing747 che Md80 implementano Aereo quindi potrebbero essere istanze di classi uguali.

Per ovviare a questo problema possiamo utilizzare l’operatore instanceof

if (aereo instanceof Md80)
  Md80 md80 = (Md80)aereo;

Cosa succede invece se facciamo cast esplicito di due oggetti non correlati tra loro ?

Boeing747 boeing = new Boeing747();
Aereo aereo = boeing;
StringBuilder sb = (StringBuilder)aereo;

otterremo un bel errore di compilazione, Java sa che StringBuilder non è in nessun modo correlato ad Aereo !

Facciamo attenzione, il cast esplicito è richiesto quando vogliamo passare da una superclasse ad una sottoclasse :

Boeing747 boeing = new Boeing747();
Aereo aereo = boeing;
Boeing747 boeing2 = aereo // FAIL !

Anche se aereo è un Boeing747, il compilatore non ci permette di eseguire un cast implicito !