1 Interfacce e polimorfismo Sandro Pedrazzini Approfondimento Interfacce e polimorfismo.

Post on 02-May-2015

236 views 5 download

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

1 Interfacce e polimorfismoSandro Pedrazzini

Approfondimento

Interfacce e polimorfismo

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

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(…)

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");

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) {...}

}

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”));

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.

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);}

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.

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.

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.

12 Interfacce e polimorfismoSandro Pedrazzini

Implementazione (2)Implementazione (2)

Icon

paintIcon(…)

WorldIcon

paintIcon(…)

ImageIcon

paintIcon(…)

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; }}

14 Interfacce e polimorfismoSandro Pedrazzini

UtilizzoUtilizzo

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

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.

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.

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”

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);

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

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}

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);...

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; }}

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()); } }}

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)

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) { ... }}

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; }}

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 }}

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; }}

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()); } }}

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.

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

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ì)

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; }}

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

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;

}

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

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

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

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; }}