====== Subtyping & wildcards ====== PREV [[java:generics:alcune_novita_rispetto_a_java_1.4]] NEXT [[java:generics:confronti_tra_elementi]] In Java un tipo A è un sottotipo di un altro tipo B se questi sono legati da una clausola **extends** o **implements** A extends B o A implements B (Integer è sottotipo di Number, List è sottotipo di Collection) Subtyping è riflessiva e transitiva: //Se A è sottotipo di B allora B è supertipo di A// Ogni tipo reference è sottotipo di Object ed Object è supertipo di ogni reference type ===== Principio di sostituzione ===== interface Collection{ ... public boolean add(E elem); ... } Principio di sostituzione: possiamo aggiungere Integer e Double a collezioni di Numbers (Integer e Double sono sottotipi di Number) List numeri = new ArrayList(); numeri.add(2); numeri.add(3.14); assert numeri.toString().equals( "[2, 3.14]"); * OK List è sottotipo di Collection * OK 2 ha tipo* Integer che è sottotipo di Number * OK 3.14 ha tipo* Double che è sottotipo di Number * Per ora tutto OK __Eccoci al dunque ...__ ===== Wildcard con extends ===== interface Collection{ ... public boolean addAll( Collection c); ... } * OK, posso inserire in una collezione di tipo E elementi di tipo E * ? extends E → OK, posso inserire in una collezione di tipo E elementi appartenenti ad una collezione di qualsiasi sottotipo di E List numeri = new ArrayList(); List interi = Arrays.asList(1,2); List doubles = Arrays.asList(2.78,3.14); numeri.addAll(interi); numeri.addAll(doubles) * OK Integer e Double sono sottotipi di Number * List è sottotipo di List (idem per Double) List interi = Arrays.asList(1,2); List numeri = interi; numeri.add(3.14); // → Errore di compilazione assert interi.toString().equals( "[1, 2, 3.14]"); List può essere una lista di qualsiasi tipo di numero! Non è detto che sia una lista di Double! :-( ===== wildcard con super ===== public static void copia(List dest, List src){ for (int i=0; i < src.size(); i++) { dest.set(i,src.get(i)); // ← Attenzione } } //? super T → lista destinazione di supertipo di T// List oggetti = Arrays.asList(2,3.14,"four"); List interi = Arrays.asList(5,6); Collections.copia(oggetti, interi); assert oggetti.toString().equals("[5, 6, four]"); ===== Get and Put Principle ===== * GET: Utilizzare il wildcard extends quando si deve solamente recuperare valori da una struttura * PUT: Utilizzare il wildcard super quando si deve solamente inserire dati in una struttura * GET and PUT: Non utilizzare i wildcard quando si deve sia prendere che inserire ==== Eccezioni al Get/Put principle ==== * ? extends E → è possibile inserire null * ? super T → è possibile prelevare Object ==== Sporco trucco di programmazione ==== public void rebox( Box box) { reboxHelper(box); } private static void reboxHelper(Box box) { box.put(box.get()); } Viene effettuata una chiamata ad un “helper” **senza** wildcard ===== Array ===== Il subtyping degli array in java è covariante: S sottotipo di T → S[] sottotipo di T[] :!: Il codice seguente __viene compilato__ Integer[] interi = new Integer[] {1,2,3}; Number[] numeri = interi; numeri[2] = 3.14; // --> oops ma attenzione che a runtime viene lanciato un errore Exception in thread "main" java.lang.ArrayStoreException: java.lang.Double // numeri--> 1 2 3.14 Passiamo adesso ai generics List interi= Arrays.asList(1,2,3); List numeri = interi; → non compila! numeri.add(3.14); il codice __non compila__ il Subtyping per i generics è controvariante: S supertipo di T → List è sottotipo di List ==== Covariante ==== S ----- extends -----> T S[] ----- extends -----> T[] String ----- extends -----> Object String[] ----- extends -----> Object[] ==== Controvariante ==== S ----- extends -----> T List ----- extends -----> List String ----- extends -----> Object List ----- extends -----> List ==== Cattura del wildcard ==== Quando viene invocato un metodo generico il parametro di tipo deve essere scelto in modo da “combaciare” con il tipo rappresentato dal wildcard → //wildcard capture// public static void reverse(List list) public static void reverse(List list) è un sinonimo di public static void reverse(List list){ List tmp = new ArrayList(list); for (int i = 0 ; i < list.size() ; i++){ list.set(i, tmp.get(list.size() -i - 1) ); } } il seguente codice __non compila__ public static void reverse(List list){ List tmp = new ArrayList(list); for (int i = 0 ; i < list.size() ; i++){ list.set(i, tmp.get(list.size() -i - 1) ); } } errore di compilazione The method set(int, capture#3-of ?) in the type List is not applicable for the arguments (int, Object) ===== Restrizioni del wildcard ===== * Creazione dell'istanza * Chiamata a metodo generico * Supertipo ==== Creazione dell'istanza ==== List l = new ArrayList(); NO! Errore di compilazione List s = new ArrayList(); OK ==== Chiamata a metodo generico ==== public class Lista { public static Listfactory() { return new ArrayList(); } } .. OK List l = Lista.factory(); OK List l2 = Lista.factory(); OK List l3 = Lista.factory(); → ERRORE List l4 = Lista.>factory(); OK ==== Supertipo ==== Class Lista extends ArrayList {…} → NO Class Lista2 implements List {…} → NO Class List3 implements ArrayList> {…} → OK NEXT [[java:generics:confronti_tra_elementi]]