La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

Programmazione Parametrica ( a.k.a. Generics )

Presentazioni simili


Presentazione sul tema: "Programmazione Parametrica ( a.k.a. Generics )"— 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<E> { 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<BankAccount> ArrayList<Measurable> ArrayList<double> // No! ArrayList<Double>

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 l’eventuale errore a run time List intList = new LinkedList(); intList.add(new Integer(0)); Integer x = (Integer) intList.get(0); Continua

8 Variabili di tipo e controlli di tipo
Container generici: più sintetici ed eleganti Compilatore può stabilire un invariante sugli elementi della lista garantire l’assenza di errori a run-time in forza di quell’invariante. List<Integer> intList = new LinkedList<Integer>(); 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<Integer> Le variabili di tipo rendono il codice parametrico più robusto e semplice da leggere e manutenere List<Integer> intList = new LinkedList<Integer>(); intList.add(new Integer(0)); Integer x = intList.get(0);

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

11 Classi parametriche: uso
Quando utilizziamo un tipo parametrico, tutte le occorrenze dei parametri formali sono rimpiazzate dall’argomento (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 Nulla di speciale, a parte la presenza dei parametri di tipo

12 Sintassi: uso GenericClassName<Type1, Type2, . . .> Esempio:
  ArrayList<BankAccount> HashMap<String, Integer> Scopo: Fornire tipo specifici per ciascuna delle variabili di tipo introdotte nella dichiarazione

13 Esempio: Pair<T,S>
Una semplice classe parametrica per rappresentare coppie di oggetti public class Pair<T, S> { 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; } Continua

14 Esempio: Pair<T,S>
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<String, BankAccount> result = new Pair<String, BankAccount> ("Harry Hacker", harrysChecking); String name = result.getFirst(); BankAccount account = result.getSecond();

15 Variabili di tipo: convenzioni
Variabile Significato Inteso E Tipo degli elementi in una collezione K Tipo delle chiavi in una mappa V Tipo dei valori in una mappa T,S,U Tipi generici

16 Esempio: LinkedList<E>
public class LinkedList<E> { 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<E>
Notiamo la struttura della classe ausiliaria che specifica la struttura dei nodi Se la classe è interna, come in questo caso, non serve alcun accorgimento all’interno di Node possiamo utilizzare il tipo E, il cui scope è tutta la classe Se invece la classe è esterna, dobbiamo renderla generica

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

19 Generics e sottotipi I meccanismi di subtyping si estendono alle classi generiche C<T> <: I<T> per qualunque T Analogamente: C<T> <: I per qualunque T Sembra tutto facile, MA . . . class C<T> implements I<T> { } class C<T> 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 l’esempio … List<Integer> li = new ArrayList<Integer>(); List<Number> ln = li; Continua

21 Generics e sottotipi Come si vede abbiamo un problema
List<Integer> li = new ArrayList<Integer>(); List<Number> ln = li; // type error ln.add(3.14); Integer i = li.get(0); // uh oh ... 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 l’assegnamento Continua

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

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

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

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<T> Collection<Object> non è il supertipo di tutte le collezioni static void printCollection(Collection<Object> c) { for (Object e:c) System.out.println(e); } Continua

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 static void printCollection(Collection<?> c) { for (Object e:c) System.out.println(e); } Continua

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

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

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

31 Risposta Collection<Integer> ci = new ArrayList<Integer>; Colletion<?> c = ci; c.add(“a string”); // non compila ci.get(0).intValue(); L’ultima istruzione invocherebbe intValue() sul primo elemento di ci ma quell’elemento ha tipo String … Il compilatore previene l’errore, rigettando la add()

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

33 Wilcards con vincoli (bounded)
Graphics e il metodo draw() Solito problema: drawAll() non può essere invocato su una List<Circle> 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<Shape> 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<? extends Shape> 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<? extends Shape> 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<? extends Shape> 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<? extends Shape> shapes) { // errore di compilazione shapes.add(new Rectangle()); }

37 Metodi Generici Metodi che dipendono da una variabile di tipo
Possono essere definiti all’interno di qualunque classe, generica o meno N.B. Evitiamo List<Object> 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){ } 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 dell’array purché sia un tipo riferimento /* trasforma un array in una lista, copiando * tutti gli elementi di a in l */ static <T> void array2List(T[] a, List<T> l) { for (T o : a) l.add(o) }

40 Invocazione di metodi generici
Nell’invocazione di un metodo generico non è necessario passare l’argomento di tipo il compilatore inferisce il tipo, se esiste, dai tipi degli argomenti del metodo

41 Invocazione di metodi generici
Object[] oa = new Object[100]; Collection<Object> co = new ArrayList<Object>(); fromArrayToCollection(oa, co); // T = Object (inferito) String[] sa = new String[100]; Collection<String> cs = new ArrayList<String>(); 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<Number> cn = new ArrayList<Number>(); 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 Continua

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

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 interface Collection<E> { public <T> boolean containsAll(Collection<T> c); public <T extends E> boolean addAll(Collection<T> c); . . . } Continua

44 Wildarcds vs variabili di tipo
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 interface Collection<E> { public static <T> void copy(List<T> dest, List<? extends T> src) . . . } Continua

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

46 Wildarcds vs variabili di tipo
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

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<String> words = new ArrayList<String>(); 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> { 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 extends Comparable<T>> T max(Collection<T> 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 extends Comparable<T>> T max(T max(List <T> coll)

53 File LinkedList.java 019: /**
019: /** 020: Returns the first element in the linked list. 021: @return 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. 032: @return 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. 045: @param 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: /**
055: /** 056: Returns an iterator for iterating through this list. 057: 058: */ 059: public ListIterator<E> 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<E> Implemental’interfaccia ListIterator<E> Ha accesso al campo first e alla classe interna Node

57 Classe LinkedListIterator
072: private class LinkedListIterator implements ListIterator<E> 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 l’iteratore sul prossimo. 086: @return 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 l’ultimo 104: visitato. 105: @return true se esiste un elemento dopo 106: l’ultimo 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: l’iteratore 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 l’elemento 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. 162: @param 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<E> 08: { 09: /** 10: Moves the iterator past the next element. 11: @return 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
18: @return 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. 26: @param 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. 39: @param element the element to set 40: */ 41: void set(E element); 42: }


Scaricare ppt "Programmazione Parametrica ( a.k.a. Generics )"

Presentazioni simili


Annunci Google