PREV Alcune novità rispetto a java 1.4 NEXT 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</java> (Integer è sottotipo di Number, List<E> è sottotipo di Collection<E>) 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 ===== <code java> interface Collection<E>{ ... 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<Number> numeri = new ArrayList<Number>(); numeri.add(2); numeri.add(3.14); assert numeri.toString().equals( "[2, 3.14]");
Eccoci al dunque …
interface Collection<E>{ ... public boolean addAll( Collection<? extends E> c); ... }
List<Number> numeri = new ArrayList<Number>(); List<Integer> interi = Arrays.asList(1,2); List<Double> doubles = Arrays.asList(2.78,3.14); numeri.addAll(interi); numeri.addAll(doubles)
List<Integer> interi = Arrays.asList(1,2); List<? extends Number> numeri = interi; numeri.ADD(3.14); // → Errore di compilazione assert interi.toString().equals( "[1, 2, 3.14]");
List<? extends Number> può essere una lista di qualsiasi tipo di numero! Non è detto che sia una lista di Double!
public static <T> void copia(List<? super T> dest, List<? extends T> 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<Object> oggetti = Arrays.<Object>asList(2,3.14,"four"); List<Integer> interi = Arrays.asList(5,6); Collections.copia(oggetti, interi); assert oggetti.toString().equals("[5, 6, four]");
public void rebox( Box<? extends Object> box) { reboxHelper(box); } private static <V> void reboxHelper(Box<V> box) { box.put(box.get()); }
Viene effettuata una chiamata ad un “helper” senza wildcard
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<Integer> interi= Arrays.asList(1,2,3); List<Number> numeri = interi; → non compila! numeri.add(3.14);
il codice non compila il Subtyping per i generics è controvariante: S supertipo di T → List<S> è sottotipo di List <? super T>
S ----- extends -----> T S[] ----- extends -----> T[] String ----- extends -----> Object String[] ----- extends -----> Object[]
S ----- extends -----> T List<T> ----- extends -----> List <? super S> String ----- extends -----> Object List<Object> ----- extends -----> List<? super String>
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 <T> void reverse(List<T> list) public static void reverse(List<?> list)
<?> è un sinonimo di <? extends Object>
public static <T> void reverse(List<T> list){ List<T> tmp = new ArrayList<T>(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<Object> tmp = new ArrayList<Object>(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<capture#3-of ?> is not applicable for the arguments (int, Object)
List<?> l = new ArrayList<?>(); NO! Errore di compilazione List<? super Number > s = new ArrayList<Number>(); OK
public class Lista { public static <T> List<T>factory() { return new ArrayList<T>(); } } .. OK List<?> l = Lista.factory(); OK List<?> l2 = Lista.<Object>factory(); OK List<?> l3 = Lista.<?>factory(); → ERRORE List<?> l4 = Lista.<List<?>>factory(); OK
Class Lista extends ArrayList<?> {…} → NO Class Lista2 implements List<?> {…} → NO Class List3 implements ArrayList<List<?>> {…} → OK