1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

39
1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo

Transcript of 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

Page 1: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

1 Interfacce e polimorfismoSandro Pedrazzini

Approfondimento

Interfacce e polimorfismo

Approfondimento

Interfacce e polimorfismo

Page 2: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

2 Interfacce e polimorfismoSandro Pedrazzini

MotivazioneMotivazione

Importanza del polimorfismo nell’utilizzo dei pattern e, piùIn generale, nella programmazione OO

Ruolo degli elementi “interface”

Disaccoppiamento degli elementi

Programmazione generica

Estensione di funzionalità esistente

Page 3: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

3 Interfacce e polimorfismoSandro Pedrazzini

InterfaceInterface

• Separazione del concetto di interfaccia da quello di classe

• Più classi possono realizzare la stessa interfaccia

Sci

calcolaImporto(…)

Carving

calcolaImporto(…)

Bambini

calcolaImporto(…)

Normale

calcolaImporto(…)

Page 4: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

4 Interfacce e polimorfismoSandro Pedrazzini

Esempio 1: genericità con l’interfaccia IconEsempio 1: genericità con l’interfaccia Icon

• Java mette a disposizione il metodo showMessageDialog(...) per mostrare un dialogo in interfaccia

JOptionPane.showMessageDialog(null,"Hello World");

Page 5: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

5 Interfacce e polimorfismoSandro Pedrazzini

Scelta dell’icona (1)Scelta dell’icona (1)

• Esistono altri overloading del metodo showMessageDialog(...). Ne scegliamo uno che ci permetta di specificare cosa mostrare come immagine nel messaggio.

class JOptionPane extends JComponent implements Accessible{ ... public static void showMessageDialog( Component parentComponent, Object message, String title, int messageType, Icon icon) {...}

}

Page 6: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

6 Interfacce e polimorfismoSandro Pedrazzini

Scelta dell’icona (2)Scelta dell’icona (2)

JOptionPane.showMessageDialog(null,"Hello World”,“message dialog”,JOptionPane.INFORMATION_MESSAGE,new ImageIcon(“lampadina.gif”));

Page 7: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

7 Interfacce e polimorfismoSandro Pedrazzini

Nuova sceltaNuova scelta

• Supponiamo ora di voler visualizzare nel messaggio una forma grafica senza dover generare precedentemente un file contenente l’immagine.

• Visto che showMessageDialog(...) accetta un elemento di tipo Icon, non siamo obbligati a passare ImageIcon, ma possiamo fornire un oggetto di qualsiasi classe che realizzi l’interfaccia Icon.

Page 8: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

8 Interfacce e polimorfismoSandro Pedrazzini

Interfaccia Icon (1)Interfaccia Icon (1)

public interface Icon{ int getIconWidth(); int getIconHeight(); void paintIcon( Component c, Graphics g, int x, int y);}

Page 9: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

9 Interfacce e polimorfismoSandro Pedrazzini

Interfaccia Icon (2)Interfaccia Icon (2)

• Un’interfaccia non contiene una realizzazione di funzionalità. Specifica semplicemente un insieme di metodi.

• Qualsiasi classe che implementi l’interfaccia Icon ha due responsabilità:

– Fornire la dimensione dell’icona

– Disegnare l’icona.

Page 10: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

10 Interfacce e polimorfismoSandro Pedrazzini

Interfaccia Icon (3)Interfaccia Icon (3)

• Il parametro Component di paintIcon() rappresenta il componente grafico che deve contenere l’immagine. Da questo è possibile ottenere alcune proprietà, come il colore dello sfondo, il font, ecc.

• È quindi possibile disegnare l’immagine nell’area grafica (Graphics) in modo che si adatti al contesto in cui viene disegnata.

Page 11: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

11 Interfacce e polimorfismoSandro Pedrazzini

Implementazione (1)Implementazione (1)

• Realizziamo una classe WorldIcon che implementa Icon.

• La classe disegna un cerchio rappresentante il mondo.

• Un oggetto di questa classe potrà essere passato al metodo showMessageDialog(), che non ha bisogno di conoscere la classe WorldIcon. Gli basta sapere che si comporta come (implementa) Icon.

Page 12: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

12 Interfacce e polimorfismoSandro Pedrazzini

Implementazione (2)Implementazione (2)

Icon

paintIcon(…)

WorldIcon

paintIcon(…)

ImageIcon

paintIcon(…)

Page 13: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

13 Interfacce e polimorfismoSandro Pedrazzini

Implementazione (2)Implementazione (2)

public class WorldIcon implements Icon{ private int fSize;

public WorldIcon(int size){ fSize = size; }

public void paintIcon(Component c, Graphics g, int x, int y) { Graphics2D g2 = (Graphics2D)g; Ellipse2D.Double world = new Ellipse2D.Double(x,y,fSize, fSize); g2.setColor(Color.BLUE); g2.fill(world); }

public int getIconWidth() { return fSize; }

public int getIconHeight() { return fSize; }}

Page 14: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

14 Interfacce e polimorfismoSandro Pedrazzini

UtilizzoUtilizzo

JOptionPane.showMessageDialog( null,"Hello World", "message dialog", JOptionPane.INFORMATION_MESSAGE, new WorldIcon(60));

Page 15: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

15 Interfacce e polimorfismoSandro Pedrazzini

Considerazioni (1)Considerazioni (1)

• Chi realizza il metodo showMessageDialog() non ha nessuna idea di quale tipo di icona verrà passata.

• Le classi utilizzate per realizzare l’icona possono essere completamente diverse. L’unica cosa in comune consiste nell’implementare l’interfaccia Icon.

• Solo quando viene chiamato in showMessageDialog() un metodo di Icon, l’interprete Java cerca di identificare il vero tipo dell’oggetto.

Page 16: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

16 Interfacce e polimorfismoSandro Pedrazzini

Considerazioni (2)Considerazioni (2)

• Il polimorfismo è caratterizzato proprio da questa capacità di selezionare il metodo appropriato per un certo oggetto.

• Un utilizzo importante del polimorfismo consiste nel fornire meccanismi che si comportino come accoppiamento rilassato.

• Nel nostro caso: il metodo showMessageDialog() non ha bisogno di nessuna informazione di come WorldIcon elabori l’immagine. È solamente interessato alle chiamate all’interfaccia Icon. Non esiste nessun accoppiamento tra JOptionPane e WorldIcon.

Page 17: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

17 Interfacce e polimorfismoSandro Pedrazzini

Considerazioni (3)Considerazioni (3)

• Quando utilizziamo la classe di una libreria di terzi, prima viene implementata la libreria, poi il nostro programma principale che la utilizza.

• Quando creiamo una nostra sottoclasse da usare in modo polimorfico, il programma principale che la utilizza può essere stato implementato da terzi ben prima che noi mettiamo a disposizione la nostra funzionalità specifica

=> Principio del “framework”

Page 18: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

18 Interfacce e polimorfismoSandro Pedrazzini

Esempio 2: ComparableEsempio 2: Comparable

• Altro esempio di codice generico con l’utilizzo del polimorfismo

• Metodo statico sort() della classe Collections, in grado di ordinare una lista qualsiasi.

List list = ...;Collections.sort(list);

Page 19: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

19 Interfacce e polimorfismoSandro Pedrazzini

Comparable (1)Comparable (1)

• I singoli oggetti della lista possono appartenere a una classe qualsiasi, a patto che implementi l’interfaccia Comparable.

public interface Comparable<T>{ int compareTo(T other);}

• La chiamata a compareTo() restituisce un valore negativo se l’oggetto invocante precede l’oggetto parametro, zero se i due oggetti sono uguali e un valore positivo

Page 20: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

20 Interfacce e polimorfismoSandro Pedrazzini

Comparable (2)Comparable (2)

• Come mai tutti gli oggetti devono essere di tipo Comparable?

• Perché l’algoritmo di sort, chiamando compareTo() riesce a decidere gli spostamenti degli oggetti, senza dover conoscere il loro vero tipo.

Comparable<...> object1 = ...;if (object1.compareTo(object2) > 0){ sposta object1 rispetto a object2}

Page 21: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

21 Interfacce e polimorfismoSandro Pedrazzini

Comparable (3)Comparable (3)

Esempio 1: String realizza Comparable

List<String> countries = new ArrayList<String>();countries.add(“Switzerland);countries.add(“Belgium”);countries.add(“Germany”);

Collections.sort(countries);...

Page 22: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

22 Interfacce e polimorfismoSandro Pedrazzini

Comparable (4)Comparable (4)Esempio 2: Realizzazione di una nuova classe

public class Country implements Comparable<Country> { private String fName; private double fArea;

public Country(String name, double area){ fName = name; fArea = area; }

public String getName() { return fName; }

public double getArea() { return fArea; }

• Criterio di ordinamento: dimensione della superficie del territorio

public int compareTo(Country other) { if (fArea < other.getArea()){ return -1; } if (fArea > other.getArea()){ return 1; } return 0; }}

Page 23: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

23 Interfacce e polimorfismoSandro Pedrazzini

Comparable (5)Comparable (5)

• Utilizzo della classe Country, con ordinamento secondo la grandezza in km2

public class CountryTry { public static void main(String[] args) { List<Country> countries = new ArrayList<Country>(); countries.add(new Country("Belgium",77000)); countries.add(new Country("Switzerland",41000)); countries.add(new Country("Uruguay",440000)); ... Collections.sort(countries);

for (Country country : countries){ System.out.println(country.getName() + " " + country.getArea()); } }}

Page 24: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

24 Interfacce e polimorfismoSandro Pedrazzini

Interfaccia Comparator (1)Interfaccia Comparator (1)

• Se ora volessimo ordinare le stesse nazioni dell’esempio 2 in base al nome, invece che in base alla superficie, dovremmo ridefinire il metodo compareTo().

• Oltre che essere scomodo, ci obbligherebbe a definire sottoclassi unicamente per distinguere diversi metodi compareTo() (modifica del design per scopi che con il design nulla hanno a che vedere)

Page 25: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

25 Interfacce e polimorfismoSandro Pedrazzini

Interfaccia Comparator (2)Interfaccia Comparator (2)

• Soluzione: utilizzo di un overloading di sort(), che accetta come secondo parametro un oggetto Comparator

• Gli oggetti presenti in List vengono ordinati in base all’ordinamento definito in Comparator.

class Collections { ... public static void sort(List<T> list, Comparator<? super T> c) { ... }}

Page 26: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

26 Interfacce e polimorfismoSandro Pedrazzini

Interfaccia Comparator (3)Interfaccia Comparator (3)

• La lista List può ora contenere oggetti di qualsiasi tipo.

• Non è più necessario che appartengano a classi che implementino un’interfaccia particolare.

public class Country { private String fName; private double fArea;

public Country(String name, double area){ fName = name; fArea = area; }

public String getName() { return fName; }

public double getArea() { return fArea; }}

Page 27: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

27 Interfacce e polimorfismoSandro Pedrazzini

Interfaccia Comparator (4)Interfaccia Comparator (4)

public class ComparatorByName implements Comparator<Country> { public int compare(Country country1, Country country2) { String name1 = country1.getName(); String name2 = country2.getName();

return name1.compareTo(name2); //implementato in String }}

Page 28: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

28 Interfacce e polimorfismoSandro Pedrazzini

Interfaccia Comparator (5)Interfaccia Comparator (5)

public class ComparatorByArea implements Comparator<Country> { public int compare(Country country1, Country country2) { String area1 = country1.getArea(); String area2 = country2.getArea();

if (area1 < area2){ return -1; } if (area1 > area2){ return 1; }

return 0; }}

Page 29: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

29 Interfacce e polimorfismoSandro Pedrazzini

Interfaccia Comparator (6)Interfaccia Comparator (6)

• Utilizzo di ComparatorByName

public class CountryTry2 { public static void main(String[] args) { List<Country> countries = new ArrayList<Country>(); countries.add(new Country("Switzerland", 15000)); countries.add(new Country("Uruguay", 170000)); countries.add(new Country("Belgium", 30000)); ... Collections.sort(countries, new ComparatorByName());

for (Country country : countries) { System.out.println(country.getName() + " " + country.getArea()); } }}

Page 30: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

30 Interfacce e polimorfismoSandro Pedrazzini

Overriding di equals() e hashCode() (1)Overriding di equals() e hashCode() (1)

• Una comune sorgente di errore in applicazioni Java consiste nel dimenticare di riscrivere il metodo hashCode() ogni volta che si riscrive equals().

• Il metodo hashCode() viene usato quando si ha a che fare con elementi di Collection, come liste e tabelle hash.

Page 31: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

31 Interfacce e polimorfismoSandro Pedrazzini

Overriding di equals() e hashCode() (2)Overriding di equals() e hashCode() (2)

• Il contratto, come specificato in Object, prevede

1. hashCode() deve restituire sempre lo stesso valore se chiamato più volte sullo stesso oggetto (non necessariamente lo stesso tra un’esecuzione e l’altra)

2. Se due oggetti sono uguali rispetto a equals(), anche i loro metodi hasCode() devono restituire lo stesso risultato

3. Se due oggetti sono diversi, rispetto a equals(), non è detto che hashCode() debba restituire risultati diversi. Se però si fa in modo che anche le hashCode() restituiscono risultati diversi, anche la performance di tabelle hash migliora

Page 32: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

32 Interfacce e polimorfismoSandro Pedrazzini

Oggetti uguali, stesso hashCodeOggetti uguali, stesso hashCode

• L’errore più frequente capita con il punto 2: oggetti uguali devono avere lo stesso valore di hash

– Capita infatti che due oggetti siano uguali dal punto di vista logico (overriding di equals()), ma dal loro valore di hash sono diversi

– Il metodo hashCode() viene ereditato da Object, che assegna un hashCode diverso ad ogni nuovo oggetto (tipicamente convertendo l’indirizzo di allocazione in un int, anche se non dev’essere necessariamente così)

Page 33: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

33 Interfacce e polimorfismoSandro Pedrazzini

Esempio (1)Esempio (1)public class Country { private String fName; private double fArea;

public Country(String name, double area){ fName = name; fArea = area; }

public String getName() { return fName; }

public double getArea() { return fArea; }

public boolean equals(Object o) { if (o == this){ return true; } if (!o instanceof Country) { return false; } Country county = (Country)o; return country.getName().equals(fName) && country.getArea() == fArea; }}

Page 34: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

34 Interfacce e polimorfismoSandro Pedrazzini

Esempio (2)Esempio (2)

• Supponiamo ora di voler usare questa classe con HashMap

Map<Country, Integer> population = new HashMap<Country, Integer>();population.put(new Country(“Switzerland”, 15000), 7000000);

Ci si aspetterebbe che un’espressione del genere restituisca il valore di 7 milioni, invece restituisce null

population.get(new Country(“Switzerland”, 15000));

gli oggetti coinvolti sono 2 e il fatto di aver omesso l’implementazione di hashCode() causa un diverso valore per istanze diverse

Page 35: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

35 Interfacce e polimorfismoSandro Pedrazzini

Esempio (3)Esempio (3)

• Bisogna allora provvedere a fornire un hashCode che sia uguale per istanze equals

• Variante semplice, ma da non usare !!

public int hashCode() {

return 150;

}

Page 36: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

36 Interfacce e polimorfismoSandro Pedrazzini

Esempio (4)Esempio (4)

• Un codice fisso è “legale” perché assicura che oggetti uguali abbiano lo stesso hashCode

• Non è una buona soluzione, perché fa in modo che TUTTI gli oggetti abbiano hashCode uguale.

• In questo modo tutti gli oggetti in una hash table avrebbero stessa chiave, facendo degenerare la tabella in una lista semplice, con i costi che ne derivano

Page 37: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

37 Interfacce e polimorfismoSandro Pedrazzini

hashCode (1)hashCode (1)

• Una buona funzione di hash tende a produrre valori diversi di hash per oggetti diversi

• Ci sono varie ricette su come generare valori di hashCode

• Alcuni elementi da considerare:– Cercare di considerare tutti i campi significativi usati in equals()

– Tralasciare i campi ridondanti (calcolati da altri campi)

– Se un campo è un array, trattarlo come se ogni elemento fosse un campo separato

Page 38: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

38 Interfacce e polimorfismoSandro Pedrazzini

hashCode (2)hashCode (2)

• Esempio tratto da Bloch, Effective Java

– Registra una costante iniziale nella variabile risultato (esempio: 17)– Per ogni campo genera un valore intero

» Per ogni elemento di tipo scalare, usare il valore effettivo» byte, char, short, int => (int) f» float => Float.floatToIntBits(f)» long => (int) (f^(f >>> 32) (xor con shift a destra di 4 byte)» Per riferimenti a oggetti che in equals() sono usati attraverso il loro equals(), usare il loro valore di

hashCode()– Combinare tutti i valori ottenuti in questo modo:

risultato = 31 * risultato + valore

Verificare che a istanze uguali corrispondano valori uguali

Il valore 31 è scelto perché numero primo. Un vantaggio di 31 consiste nel fatto che la moltiplicazione può essere rimpiazzata da uno shift e sottrazione, più performanti.

31 * i == (i << 5) -i

Moderne VM eseguono automaticamente questo tipo di ottimizzazione

Page 39: 1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

39 Interfacce e polimorfismoSandro Pedrazzini

EsempioEsempiopublic class Country { private String fName; private double fArea;

...

public boolean equals(Object o) { if (o == this){ return true; } if (!o instanceof Country) { return false; } Country county = (Country)o; return country.getName().equals(fName) && country.getArea() == fArea; }

public int hashCode() { int result = 17; result = 31 * result + fName.hashCode(); long areaCode = Double.doubleToLongBits(fArea); result = 31 * result + (int)(areaCode ^(areaCode >>> 32)); return result; }}