11.06.2013 Views

Subtyping e Polimorfismo

Subtyping e Polimorfismo

Subtyping e Polimorfismo

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

Subtype Polymorphism<br />

Conversioni di tipo<br />

• Variabile: locazione con un tipo associato<br />

Tipo della variabile determinato dal compilatore<br />

guardando la dichiarazione<br />

Una variabile di tipo reference contiene un riferimento<br />

ad un oggetto<br />

• Oggetto: istanza di una classe<br />

Tipo dell’oggetto: la classe che lo crea<br />

Determinato a run time<br />

• Una variabile può assumere come valori<br />

riferimenti ad oggetti di classi diverse<br />

Conversioni di tipo<br />

Continua…<br />

• La conversione è lecita solo in determinate<br />

situazioni<br />

Measurable x = new Rectangle(5, 10, 20, 30); // ERRORE<br />

• Problema: Rectangle non implementa<br />

Measurable<br />

• Interfacce e subtype polimorfismo<br />

Tipi, sottotipi e conversioni di tipo<br />

<strong>Polimorfismo</strong> e dinamic dispatch<br />

Conversioni di tipo<br />

• È possibile assegnare un riferimento di tipo<br />

classe ad una variabile di tipo interfaccia<br />

purchè la classe implementi l’interfaccia<br />

BankAccount account = new BankAccount(10000);<br />

Measurable x = account; // OK<br />

Coin dime = new Coin(0.1, "dime");<br />

Measurable y = dime; // OK<br />

<strong>Subtyping</strong><br />

Continua…<br />

• <strong>Subtyping</strong> (


<strong>Subtyping</strong><br />

• Principio di sostituibilità<br />

Un riferimento di un sottotipo può essere usato<br />

ovunque ci si aspetti un riferimento di un supertipo<br />

• Le regole di sottotipo devono garantire la<br />

correttezza del principio di sostituibilità<br />

<strong>Subtyping</strong> e regole di typing<br />

Continua…<br />

• Regola di assegnamento<br />

Un riferimento di tipo T1 si puo sempre assegnare ad una variabile<br />

di tipo T2 sse T1


<strong>Polimorfismo</strong> – dynamic dispatch<br />

• Possiamo invocare ognuno dei metodi<br />

dell’interfaccia:<br />

double m = x.getMeasure();<br />

• Quale metodo invoca?<br />

Domande<br />

5. È impossibile costruire un oggetto di tipo<br />

Measurable.Perché?<br />

6. Perché invece é possibile dichiarare una<br />

variabile di tipo Measurable?<br />

<strong>Polimorfismo</strong> – dynamic dispatch<br />

• Dipende dal riferimento corrente<br />

memorizzato nella variabile<br />

• Se x riferisce un BankAccount, invoca il<br />

metodo BankAccount.getMeasure()<br />

• Se x riferisce un Coin, invoca il metodo<br />

Coin.getMeasure()<br />

• <strong>Polimorfismo</strong> (molte forme):<br />

il comportamento varia, e dipende dal tipo dinamico<br />

della variabile<br />

Risposte<br />

5. Measurable è una interfaccia. Le<br />

interfacce non hanno campi o<br />

implementazione di metodo.<br />

Ancora un esempio Forme grafiche<br />

• Costruiamo una applicazione per disegnare<br />

un insieme di forme geometriche contenute<br />

in una componente grafico:<br />

definiamo GWin, una classe che descrive un<br />

contenitore di forme geometriche disegnate<br />

mediante una invocazione del metodo paint()<br />

per esemplificare, consideriamo due tipi di forme:<br />

Car e Smiley<br />

Continua…<br />

6. Perché Measurable è un tipo: la variabile<br />

non riferirà mai una istanza di Measurable,<br />

(le interfacce non hanno istanze) ma<br />

piuttosto oggetto di una qualche classe che<br />

implementa l’interfaccia Measurable.<br />

Continua…<br />

class Car<br />

{<br />

. . .<br />

public void draw(Graphics2D g)<br />

{<br />

// Istruzioni per il disegno<br />

. . .<br />

}<br />

} class Smiley<br />

{<br />

. . .<br />

public void draw(Graphics2D g)<br />

{<br />

// Istruzioni per il disegno<br />

. . .<br />

}<br />

}<br />

3


GWin<br />

• Un contenitore di Cars e Smileys<br />

/**<br />

Una finestra che contiene un insieme Cars e Smileys<br />

*/<br />

class GWin<br />

{<br />

/**<br />

Disegna tutte le forme di questo component<br />

*/<br />

public void paint(){ /* disegna su g */ }<br />

/**<br />

Componente grafica su cui disegnare<br />

*/<br />

private Graphics2D g;<br />

}<br />

Risposte<br />

• definiamo una nuova interfaccia: Shape<br />

• Ridefiniamo le classi Car e Smiley in modo<br />

che implementino Shape<br />

• Memorizziamo gli oggetti della componente<br />

in una ArrayList<br />

GWin<br />

interface Shape { void draw(Graphics2D g); }<br />

Mantiene una ArrayList<br />

class GWin<br />

{<br />

private Graphics2D g;<br />

private ArrayList shapes;<br />

// crea una GWin con un insieme di forme<br />

public GWin(Shape... shapes)<br />

{<br />

Graphics2D g = new Graphics2D();<br />

this.shapes = new ArrayList();<br />

for (Shape s:shapes) this.shapes.add(s);<br />

}<br />

// disegna tutte le componenti della GWin<br />

public void paint()<br />

{<br />

for (Shape s:shapes) s.draw(g);<br />

}<br />

}<br />

Domanda<br />

• Che struttura utilizziamo per memorizzare le<br />

forme contenute nella GWin?<br />

• Come definiamo il metodo paint() in modo<br />

che disegni tutte le forme della componente?<br />

Car e Smiley implementano Shape<br />

class Car implements Shape<br />

{<br />

}<br />

. . .<br />

public void draw(Graphics2D g)<br />

{<br />

// Istruzioni per il disegno<br />

. . .<br />

}<br />

class Smiley implements Shape<br />

Diagramma delle Classi<br />

GWin<br />

{<br />

}<br />

. . .<br />

public void draw(Graphics2D g)<br />

{<br />

// Istruzioni per il disegno<br />

. . .<br />

}<br />

Car<br />

Shape<br />

Smiley<br />

4


So long, for today<br />

Dynamic dispatch in GWin<br />

class GWin<br />

{<br />

. . .<br />

private ArrayList shapes;<br />

. . .<br />

public void paint()<br />

{<br />

// disegna tutte le componenti della GWin<br />

// il metodo invocato effettivamente da ogni<br />

// messaggio s.draw(g) dipende dalla classe<br />

// di cui s è istanza ed è deciso a runtime<br />

for (Shape s:shapes) s.draw(g);<br />

}<br />

. . . .<br />

}<br />

Dynamic dispatch vs overloading<br />

interface I<br />

{<br />

public String m(boolean b);<br />

public String m(double d);<br />

}<br />

class A implements I<br />

{<br />

public String m(boolean b) { return “A.m(boolean)”; }<br />

public String m(double d) { return “A.m(double)”; }<br />

}<br />

class B implements I<br />

{<br />

public String m(boolean b) { return “B.m(boolean)”; }<br />

public String m(double d) { return “B.m(double)”; }<br />

}<br />

<strong>Polimorfismo</strong> – dynamic dispatch<br />

• Dynamic dispatch:<br />

Il metodo da invocare per rispondere ad un<br />

messaggio è deciso a tempo di esecuzione<br />

Dynamic dispatch vs overloading<br />

• Dynamic dispatch:<br />

Il metodo da invocare per rispondere ad un<br />

messaggio è deciso a tempo di esecuzione<br />

• Notiamo bene<br />

Il metodo da invocare è deciso a runtime<br />

il compilatore decide se esiste un metodo da invocare<br />

• Overloading:<br />

Nel caso esista più di un metodo, il compilatore<br />

decide staticamente il tipo del metodo da invocare<br />

Dynamic dispatch vs overloading<br />

class Client<br />

{<br />

public void static show(I x)<br />

{<br />

// tipo del metodo invocato = m(boolean)<br />

// deciso dal compilatore staticamente<br />

// metodo invocato deciso dinamicamente<br />

// in funzione del tipo dell’argomento<br />

// passato per x<br />

System.out.println( x.m(false) );<br />

}<br />

}<br />

5


Domanda<br />

• Che cosa hanno in comune i meccanismi di<br />

overloading e di dynamic dispatch? In cosa<br />

sono diversi?<br />

<strong>Subtyping</strong> e sostituibilità<br />

• Principio di sostituibilità<br />

Un riferimento di un sottotipo può essere usato<br />

ovunque ci si aspetti un riferimento di un supertipo<br />

• Le regole di sottotipo garantiscono la<br />

correttezza del principio di sostituibilità<br />

Tutti i metodi del supertipo devono essere<br />

implementati dal sottotipo<br />

Il sottotipi può avere anche altri metodi<br />

Continua…<br />

Car e Smiley implementano Shape<br />

class Car implements Shape<br />

{<br />

}<br />

. . .<br />

public void draw(Graphics2D g){ . . . }<br />

public String brand() {. . . }<br />

class Smiley implements Shape<br />

{<br />

}<br />

. . .<br />

public void draw(Graphics2D g){ . . . }<br />

public String mood() {. . . }<br />

Risposta<br />

• Entrambi i meccanismi contribuiscono a<br />

decidono quale metodo eseguire in risposta<br />

ad un messaggio, ma<br />

• Nell’overloading scelta è relativa al tipo del metodo,<br />

ed è fatta in compilazione guardando il tipo dei<br />

parametri<br />

• Nel dynamic dispatch la scelta è relativa al corpo del<br />

metodo, ed è fatta in esecuzione guardando il tipo<br />

dell’oggetto che riceve il messaggio<br />

<strong>Subtyping</strong> e perdita di informazione<br />

• Principio di sostituibilità<br />

Un riferimento di un sottotipo può essere usato<br />

ovunque ci si aspetti un riferimento di un supertipo<br />

• Può causare perdita di informazione<br />

nel contesto in cui ci aspettiamo il supertipo, non<br />

possiamo usare solo I metodi del supertipo<br />

perdiamo la possibilità di utilizzare gli eventuali metodi<br />

aggiuntivi del sottotipo<br />

Continua…<br />

<strong>Subtyping</strong> e perdita di informazione<br />

• Consideriamo<br />

public static void printBrand(List l)<br />

{<br />

}<br />

for (Shape s : l)<br />

// stampa la marca di tutte le macchine di l<br />

// ???<br />

Continua…<br />

6


<strong>Subtyping</strong> e perdita di informazione<br />

• Certamente non possiamo fare così …<br />

public static void printBrand(List l)<br />

{<br />

}<br />

Cast<br />

for (Shape s : l)<br />

// stampa la marca di tutte le macchine di l<br />

System.out.println( s.brand() ); // TYPE ERROR!<br />

Continua…<br />

• Tipo statico e tipo dinamico di una variabile<br />

tipo statico: quello dichiarato<br />

tipo dinamico: il tipo del riferimento assegnato alla<br />

variabile<br />

• (T)var causa errore<br />

Cast<br />

in compilazione<br />

se T non è compatibile con il tipo statico di var<br />

in esecuzione (ClassCastException)<br />

se T non è compatibile con il tipo dinamico di var<br />

Shape s = new Car();<br />

• OK: Car sottotipo di Shape<br />

Smiley c = (Smiley) s<br />

• Compila correttamente<br />

il tipo dichiarato di s è Shape<br />

Smiley e Shape sono compatibili<br />

• Errore a run time<br />

s non è uno Smiley<br />

Continua…<br />

Continua…<br />

Cast<br />

• Permette di modificare il tipo associato ad una<br />

espressione<br />

((Car)s).brand()<br />

• Un cast è permesso dal compilatore solo se applica<br />

conversioni tra tipi compatibili<br />

• Compatibili = sottotipi (per il momento)<br />

• Anche quando permesso dal compilatore, un cast<br />

può causare errore a run time<br />

• Se s non è un Car errore a run time<br />

Cast<br />

Shape s = new Car();<br />

• OK: Car sottotipo di Shape<br />

Car c = (Car) s<br />

• Compila correttamente<br />

il tipo dichiarato di s è Shape<br />

Car e Shape sono compatibili<br />

• Esegue correttamente<br />

s è un Car (il tipo dinamico di s è Car)<br />

Cast<br />

• Attenzione anche qui …<br />

public static void printBrand(List l)<br />

{<br />

}<br />

for (Shape s : l)<br />

Continua…<br />

Continua…<br />

// ClassCastException se s instance of Smiley<br />

System.out.println( ((Car)s.)brand() );<br />

Continua…<br />

7


instanceof<br />

• Permette di determinare il tipo dinamico di una<br />

variabile<br />

x istanceof T è true solo se x ha tipo dinamico T<br />

• Quindi permette di evitare errori in esecuzione<br />

if (x instanceof T) return (T) x<br />

• Esegue correttamente, perchè x è sicuramente un T<br />

Domanda<br />

• E se volessimo disegnare solo le Shapes che<br />

sono Cars?<br />

Cast<br />

• Questo, finalmente, è corretto<br />

public static void printBrand(List l)<br />

{<br />

}<br />

Risposta<br />

for (Shape s : l)<br />

if (s instanceof Car)<br />

System.out.println( ((Car)s.)brand() );<br />

// disegna tutte le Cars della GWin<br />

public void paint(){<br />

for (Shape c:shapes)<br />

if (c instanceof Car) c.draw(g);<br />

}<br />

8

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!