La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

Programmazione Parametrica ( a.k.a. Generics ). Introduzione ai meccanismi e concetti della programmazione parametrica Generics e relationi di sottotipo.

Presentazioni simili


Presentazione sul tema: "Programmazione Parametrica ( a.k.a. Generics ). Introduzione ai meccanismi e concetti della programmazione parametrica Generics e relationi di sottotipo."— Transcript della presentazione:

1 Programmazione Parametrica ( a.k.a. Generics )

2 Introduzione ai meccanismi e concetti della programmazione parametrica Generics e relationi di sottotipo wildcards generics e vincoli Implementazione di classi e metodi parametrici Supporto per i generics nella JVM

3 Programmazione polimorfa Polimorfo ~ multiforme, di molti tipi Programmazione polimorfa: creazione di costrutti (classi e metodi) che possono essere utilizzati in modo uniforme su dati di tipo diverso In Java, tradizionalmente ottenuta mediante i meccanismi di sottotipo ed ereditarietà Da Java 1.5. anche mediante i meccanismi di parametrizzazione di tipo (a.k.a. generics)

4 Variabili di Tipo Le variabili (o parametri) di tipo pemettono di creare astrazioni di tipo Classico caso di utilzzo nelle classi Container E = variabile di tipo astrae (e rappresenta) il tipo delle componenti public class ArrayList { public ArrayList() {... } public void add(E element) {... }... } Continua

5 Variabili di Tipo Possono essere istanziate con tipi classe o interfaccia Vincolo: tipi che istanziano variabili di tipo non possono essere primitivi (devono essere tipi riferimento) Classi wrapper utili allo scopo ArrayList ArrayList ArrayList // No! ArrayList

6 Variabili di tipo e controlli di tipo Utilizzare variabili di tipo nella programmazione permette maggiori controlli sulla correttezza dei tipi in fase di compilazione Aumenta quindi la solidità e robustezza del codice Continua

7 Variabili di tipo e controlli di tipo Un classico caso di utilizzo di containers Il cast è problematico, per vari motivi verboso, fonte di errori a run time Ma necessario per la compilazione e per localizzare leventuale errore a run time Continua List intList = new LinkedList(); intList.add(new Integer(0)); Integer x = (Integer) intList.get(0);

8 Variabili di tipo e controlli di tipo Container generici: più sintetici ed eleganti Compilatore può stabilire un invariante sugli elementi della lista garantire lassenza di errori a run-time in forza di quellinvariante. List intList = new LinkedList (); intList.add(new Integer(0)); Integer x = intList.get(0); Continua

9 Variabili di tipo e controlli di tipo Ora non è possibile aggiungere una stringa ad intlist:List Le variabili di tipo rendono il codice parametrico più robusto e semplice da leggere e manutenere List intList = new LinkedList (); intList.add(new Integer(0)); Integer x = intList.get(0);

10 Classi parametriche: definizione // nulla di particolare, a parte i parametri // tra parentesi angolate public interface List { void add(E x); Iterator iterator(); } public interface Iterator { F next(); boolean hasNext(); } Un frammento delle interfacce List e Iterator nel package java.util.*

11 Classi parametriche: uso Quando utilizziamo un tipo parametrico, tutte le occorrenze dei parametri formali sono rimpiazzate dallargomento (parametro attuale) Meccanismo simile a quello del passaggio dei parametri in un metodo Diversi usi generano tipi diversi Ma... classi parametriche compilate una sola volta danno luogo ad un unico file.class

12 Sintassi: uso GenericClassName Esempio: ArrayList HashMap Scopo: Fornire tipo specifici per ciascuna delle variabili di tipo introdotte nella dichiarazione

13 public class Pair { public Pair(T firstElement, S secondElement) { first = firstElement; second = secondElement; } public T getFirst() { return first; } public S getSecond() { return second; } private T first; private S second; } Esempio: Pair Una semplice classe parametrica per rappresentare coppie di oggetti Continua

14 Esempio: Pair Una semplice classe parametrica per rappresentare coppie di oggetti: I metodi getFirst e getSecond restituiscono il primo e secondo elemento, con i tipi corrispondenti Pair result = new Pair ("Harry Hacker", harrysChecking); String name = result.getFirst(); BankAccount account = result.getSecond();

15 Variabili di tipo: convenzioni VariabileSignificato Inteso ETipo degli elementi in una collezione KTipo delle chiavi in una mappa VTipo dei valori in una mappa T,S,UTipi generici

16 Esempio: LinkedList public class LinkedList {... public E removeFirst() { if (first == null) throw new NoSuchElementException(); E element = first.data; first = first.next; return element; }... private Node first; private class Node { E data; Node next; } } Continua

17 Esempio: LinkedList Notiamo la struttura della classe ausiliaria che specifica la struttura dei nodi Se la classe è interna, come in questo caso, non serve alcun accorgimento allinterno di Node possiamo utilizzare il tipo E, il cui scope è tutta la classe Se invece la classe è esterna, dobbiamo renderla generica

18 Esempio: LinkedList class Node { F data; Node next; } public class LinkedList {... public E removeFirst() { if (first == null) throw new NoSuchElementException(); E element = first.data; first = first.next; return element; }... private Node first; } Continua

19 Generics e sottotipi I meccanismi di subtyping si estendono alle classi generiche C per qualunque T Analogamente: C <: I per qualunque T Sembra tutto facile, MA... class C implements I {... }

20 Generics e sottotipi Consideriamo La prima istruzione è legale, la seconda è più delicata … Number è una classe che ha Integer, Double e altre classi wrapper come sottotipi Per capire se la seconda istruzione sia da accettare come legale continuiamo con lesempio … Continua List li = new ArrayList (); List ln = li;

21 Generics e sottotipi Come si vede abbiamo un problema nella terza istruzione inseriamo un Double nella quarta estraiamo un Integer ! Il vero problema è nella seconda istruzione soluzione: errore di compilazione per lassegnamento Continua List li = new ArrayList (); List ln = li; // type error ln.add(3.14); Integer i = li.get(0); // uh oh...

22 Generics e sottotipi In generale, dati due tipi A e B, ed tipo generico C abbiamo che: Quindi, per le stesse ragioni di prima Come abbiamo visto questo è necessario per garantire la correttezza Continua A B NON implica C C Set NON è sottotipo di Set

23 Generics e sottotipi In generale, dati due tipi A e B, ed tipo generico C abbiamo che: MA … Continua A B NON implica C C A B implica A[] B[]

24 Generics e sottotipi Continua Integer[] ai = new Integer[10] Number[] an = ai; // type OK an[0] = 3.14; // ArrayStoreException Integer i = ai[0]; // uh oh...

25 Generics e sottotipi Le limitazione sulle relazioni di sottotipo sono contro-intuitive uno degli aspetti più complessi dei generics Non solo … sono spesso anche troppo restrittive illustriamo con un esempio Continua

26 Generics e sottotipi Stampa degli elementi di una collezione Primo tentativo Inutile per stampare gli elementi di una generica Collection Collection non è il supertipo di tutte le collezioni Continua static void printCollection(Collection c) { for (Object e:c) System.out.println(e); }

27 Wildcards Stampa degli elementi di una collezione Secondo tentativo Collection è il supertipo di tutte le Collections la wildcard ? indica un qualche tipo, non specificato Continua static void printCollection(Collection c) { for (Object e:c) System.out.println(e); }

28 Wildcards Possiamo estrarre gli elementi di c al tipo Object Corretto perché, qualunque sia il loro vero tipo, sicuramente è sottotipo di Object Continua void printCollection(Collection c) { for (Object e:c) System.out.println(e); }

29 Wildcards Continua Collection c = new ArrayList (); c.add(new String()); // errore di compilazione! Daltra parte … Poichè non sappiamo esattamente quale tipo indica ?, non possiamo inserire elementi nella collezione In generale, non possiamo modificare valori che hanno tipo ?

30 Date un esempio di codice che causerebbe errore in esecuzione se permettessimo di aggiungere elementi a Collection Domanda

31 Lultima istruzione invocherebbe intValue() sul primo elemento di ci ma quellelemento ha tipo String … Il compilatore previene lerrore, rigettando la add() Risposta Collection ci = new ArrayList ; Colletion c = ci; c.add(a string); // non compila ci.get(0).intValue();

32 Wilcards con vincoli ( bounded ) Shapes : (again!) interface Shape { public void draw(Graphics g); } class Circle extends Shape { private int x, y, radius; public void draw(Graphics g) {... } } class Rectangle extends Shape { private int x, y, width, height; public void draw(Graphics g) {... } } Continua

33 Wilcards con vincoli ( bounded ) Graphics e il metodo draw() Solito problema: drawAll() non può essere invocato su una List public class Graphics { // disegna una shape public void draw(Shape s) { s.draw(this); } // disegna tutte le shapes di una lista public void drawAll(List shapes) { for (Shape s:shapes) s.draw(this) }... } Continua

34 Bounded Wilcards Quello che ci serve è un metodo che accetti liste di qualunque (sotto) tipo di Shape List bounded wildcard indica un tipo sconosciuto, sottotipo di Shape il bound può essere qualunque tipo riferimento (classe o interfaccia) Ora il metodo ha la flessibilità necessaria e desiderata void drawAll(List shapes) {... } Continua

35 Bounded Wilcards Graphics e il metodo draw() public class Graphics { // disegna una shape public void draw(Shape s) { s.draw(this); } // disegna tutte le shapes di una lista public void drawAll(List shapes) { for (Shape s:shapes) s.draw(this) }... } Continua

36 Bounded Wilcards Attenzione: cè sempre un prezzo da pagare Non possiamo modificare strutture con questi tipi [ perché? ] void addRectangle(List shapes) { // errore di compilazione shapes.add(new Rectangle()); }

37 Metodi che dipendono da una variabile di tipo Possono essere definiti allinterno di qualunque classe, generica o meno N.B. Evitiamo List perché renderebbe il metodo non utilizzabie su liste arbitrarie /* trasforma un array in una lista, copiando * tutti gli elementi di a in l */ static void array2List(Object[] a, List l){... } Metodi Generici Continua

38 Metodi Generici Al solito però non possiamo aggiungere elementi ad una struttura (o modificare) con elementi di tipo wildcard /* trasforma un array in una lista, copiando * tutti gli elementi di a in l */ static void array2List(Object[] a, List l) { for (Object o : a) l.add(o) // compiler error } Continua

39 Metodi Generici Soluzione: rendiamo il metodo parametrico possiamo invocare questo metodo con una qualunque lista il cui tipo sia supertipo del tipo base dellarray purché sia un tipo riferimento /* trasforma un array in una lista, copiando * tutti gli elementi di a in l */ static void array2List(T[] a, List l) { for (T o : a) l.add(o) }

40 Invocazione di metodi generici Nellinvocazione di un metodo generico non è necessario passare largomento di tipo il compilatore inferisce il tipo, se esiste, dai tipi degli argomenti del metodo

41 Invocazione di metodi generici Continua Object[] oa = new Object[100]; Collection co = new ArrayList (); fromArrayToCollection(oa, co); // T = Object (inferito) String[] sa = new String[100]; Collection cs = new ArrayList (); fromArrayToCollection(sa, cs); // T = String (inferito) fromArrayToCollection(sa, co); // T = Object (inferito) Integer[] ia = new Integer[100]; Float[] fa = new Float[100]; Number[] na = new Number[100]; Collection cn = new ArrayList (); fromArrayToCollection(ia, cn); // T = Number (inferito) fromArrayToCollection(fa, cn); // T = Number (inferito) fromArrayToCollection(na, cn); // T = Number (inferito) fromArrayToCollection(na, co); // T = Object (inferito) fromArrayToCollection(na, cs); // compiler error

42 Wildarcds vs variabili di tipo Ci sono situazioni in cui è possibili usare equivalentemente wildcards e variabili di tipo. Nella libreria Collection troviamo Continua interface Collection { public boolean containsAll(Collection c); public boolean addAll(Collection c);... }

43 Wildarcds vs variabili di tipo Queste specifiche possono essere espresse equivalentemente con metodi parametrici Il secondo metodo è parametrico in qualunque sottotipo di E i bounds si possono utilizzare anche con variabili, non solo con wildcards Continua interface Collection { public boolean containsAll(Collection c); public boolean addAll(Collection c);... }

44 Wildcards e variabili di tipo possono coesistere Notiamo la dipendenza tra i tipi dei due parametri: il tipo della sorgente deve essere un sottotipo del tipo della destinazione Wildarcds vs variabili di tipo Continua interface Collection { public static void copy(List dest, List src)... }

45 Potremmo analogamente riformulare in modo da evitare le wildcards Come scegliere tra le due soluzioni? Wildarcds vs variabili di tipo Continua interface Collection { public static void copy( dest, List src)... }

46 In generale, preferiamo le wildcards quando entrambe le soluzioni sono possibili Possiamo darci la seguente rule of thumb se una variabile di tipo ha una unica occorrenza nella specifica di un metodo e il tipo non è il target di un operazione di modifica utilizziamo una wildcard al posto della variabile Wildarcds vs variabili di tipo

47 Generics e erasure I tipi generici sono significativi a compile-time La JVM opera invece con tipi raw Il tipo raw è ottenuto da un tipo generico mediante un processo detto erasure che rimuove le variabili di tipo il bycode generato da un tipo generico è lo stesso che viene generato dal corrispondente tipo raw.

48 Generics e erasure Generano lo stesso bytecode List words = new ArrayList (); words.add(hi); words.add(there); String welcome = words.get(0) + words.get(1); List words = new ArrayList(); words.add(hi); words.add(there); String welcome = (String)words.get(0) + (String)words.get(1);

49 Generics e erasure Cast-iron guarantee i cast impliciti che vengono aggiunti dalla compilazione di codice generico non falliscono mai.

50 Generics e Array Non è possibile creare array generici Ecco perché: class MyClass { T[] contents = new T[100]; // Non compila public void showTheProblem() { Object[] objs = contents; objs[0] = new String(); // no ArrayStoreException T bump = contents[0]; // ClassSclassException }

51 Variabili di Tipo e Bounds Abbiamo visto che possiamo definire bounds anche per variabili di tipo (non solo wildcards) Un caso paradigmatico public static > T max(Collection coll) { T candidate = coll.iterator().next(); for (T e : coll) if candidate.compareTo(e) < 0) candidate = e; return candidate; }

52 Variabili di Tipo e Bounds Il bound su una variabile impone vincoli sulla variabile, determinando quali metodi possono essere utilizzati su valori del tipo variabile Qui il bound è ricorsivo: informa che i valori con cui operiamo forniscono un metodo compareTo() che gli argomenti del metodo devono essere dello stesso tipo dei valori public static > T max(T max(List coll)

53 File LinkedList.java 019: /** 020: Returns the first element in the linked list. the first element in the linked list 022: */ 023: public E getFirst() 024: { 025: if (first == null) 026: throw new NoSuchElementException(); 027: return first.data; 028: } 029: 030: /** 031: Removes the first element in the linked list. the removed element 033: */ 034: public E removeFirst() 035: { Continua

54 File LinkedList.java 036: if (first == null) 037: throw new NoSuchElementException(); 038: E element = first.data; 039: first = first.next; 040: return element; 041: } 042: 043: /** 044: Adds an element to the front of the linked list. element the element to add 046: */ 047: public void addFirst(E element) 048: { 049: Node newNode = new Node(element,first); 052: first = newNode; 053: } Continua

55 File LinkedList.java 054: 055: /** 056: Returns an iterator for iterating through this list. 057: 058: */ 059: public ListIterator listIterator() 060: { 061: return new LinkedListIterator(); 062: } 063: 064: private Node first; 065: 066: private class Node 067: { 068: E data; 069: Node next; 070: } 071: Continua

56 LinkedListIterator La definiamo come classe interna di LinkedList Implementalinterfaccia ListIterator Ha accesso al campo first e alla classe interna Node

57 Classe LinkedListIterator 072: private class LinkedListIterator implements ListIterator 073: { 074: /** 075: Costruisce un iteratore posizionato sul primo 076: elemento della lista 077: */ 078: public LinkedListIterator() 079: { 080: last = null; // ultimo nodo visitato 081: previous = null; // precedente a position 082: } 083: Continua

58 LinkedListIterator – next() 084: /** 085: Posizione literatore sul prossimo. il prossimo elemento 087: */ 088: public E next() 089: { 090: if (!hasNext()) 091: throw new NoSuchElementException(); 092: previous = last; // Remember for remove 093: 094: if (last == null) 095: last = first; 096: else 097: last = last.next; 098: 099: return last.data; 100: } 101: 102: Continua

59 LinkedListIterator – hasNext() 102: /** 103: Testa se cè un elemento dopo lultimo 104: visitato. true se esiste un elemento dopo 106: lultimo visitato 107: */ 108: public boolean hasNext() 109: { 110: if (last == null) 111: return first != null; 112: else 113: return last.next != null; 114: } 115: 116: Continua

60 LinkedListIterator – add() 117: /** Aggiunge un elemento dopo last e sposta 118: literatore sul prossimo elemento. 119: */ 121: public void add(E element) 122: { 123: if (last == null) 124: { 125: addFirst(element); last = first; 127: } 128: else 129: { 130: Node newNode = new Node(); 131: newNode.data = element; 132: newNode.next = last.next;// (1) 133: last.next = newNode; // (2) 134: last = newNode; // (3) 135: } 136: previous = last; // (4) 137: } Continua

61 LinkedListIterator – add() last

62 LinkedListIterator – remove() 140: /** Rimuove lelemento puntato da last. Può essere 141: invocato solo dopo una chiamata a next() 142: */ 143: public void remove() 144: { 145: if (previous == last) 146: throw new IllegalStateException(); 147: 148: if (last == first) 149: { 150: removeFirst(); 151: } 152: else 153: { 154: previous.next = last.next; // (1) 155: } 156: last = previous; // (2) 157: } Continua

63 LinkedListIterator –remove() last

64 Classe LinkedListIterator 159: /** 160: Sets the last traversed element to a different 161: value. element the element to set 163: */ 164: public void set(E element) 165: { 166: if (position == null) 167: throw new NoSuchElementException(); 168: position.data = element; 169: } 170: 171: private Node position; 172: private Node previous; 173: } // end LinkedListIterator 174: } // end LinkedList

65 File ListIterator.java 01: /** 02: A list iterator allows access of a position in a linked 03: list. This interface contains a subset of the methods 04: of the standard java.util.ListIterator interface. The 05: methods for backward traversal are not included. 06: */ 07: public interface ListIterator 08: { 09: /** 10: Moves the iterator past the next element. the traversed element 12: */ 13: E next(); 14: 15: /** 16: Tests if there is an element after the iterator 17: position. Continua

66 File ListIterator.java true if there is an element after the iterator 19: position 20: */ 21: boolean hasNext(); 22: 23: /** 24: Adds an element before the iterator position 25: and moves the iterator past the inserted element. element the element to add 27: */ 28: void add(E element); 29: 30: /** 31: Removes the last traversed element. This method may 32: only be called after a call to the next() method. 33: */ Continua

67 File ListIterator.java 34: void remove(); 35: 36: /** 37: Sets the last traversed element to a different 38: value. element the element to set 40: */ 41: void set(E element); 42: }


Scaricare ppt "Programmazione Parametrica ( a.k.a. Generics ). Introduzione ai meccanismi e concetti della programmazione parametrica Generics e relationi di sottotipo."

Presentazioni simili


Annunci Google