Risposte:
covarianza:
class Super {
Object getSomething(){}
}
class Sub extends Super {
String getSomething() {}
}
Sub # getSomething è covariante perché restituisce una sottoclasse del tipo restituito di Super # getSomething (ma completa il contratto di Super.getSomething ())
controvarianza
class Super{
void doSomething(String parameter)
}
class Sub extends Super{
void doSomething(Object parameter)
}
Sub # doSomething è controvariante perché prende un parametro di una superclasse del parametro di Super # doSomething (ma, di nuovo, soddisfa il contratto di Super # doSomething)
Avviso: questo esempio non funziona in Java. Il compilatore Java sovraccaricherà e non sovrascriverà il metodo doSomething (). Altre lingue supportano questo stile di controvarianza.
Generics
Questo è possibile anche per i generici:
List<String> aList...
List<? extends Object> covariantList = aList;
List<? super String> contravariantList = aList;
Ora puoi accedere a tutti i metodi covariantList
che non richiedono un parametro generico (poiché deve essere qualcosa che "estende Object"), ma i getter funzioneranno bene (poiché l'oggetto restituito sarà sempre di tipo "Object")
È vero il contrario per contravariantList
: puoi accedere a tutti i metodi con parametri generici (sai che deve essere una superclasse di "String", quindi puoi sempre passarne uno), ma nessun getter (il tipo restituito può essere di qualsiasi altro supertipo di String )
Co-varianza: iterabile e iteratore. Ha quasi sempre senso definire una co-variante Iterable
o Iterator
. Iterator<? extends T>
può essere usato proprio come Iterator<T>
- l'unico posto in cui appare il parametro type è il tipo restituito dal next
metodo, quindi può essere tranquillamente convertito T
. Ma se hai S
estensioni T
, puoi anche assegnarle Iterator<S>
a una variabile di tipo Iterator<? extends T>
. Ad esempio, se stai definendo un metodo di ricerca:
boolean find(Iterable<Object> where, Object what)
non potrai chiamarlo con List<Integer>
e 5
, quindi è meglio definito come
boolean find(Iterable<?> where, Object what)
Controvarianza: comparatore. Ha quasi sempre senso da usare Comparator<? super T>
, perché può essere usato proprio come Comparator<T>
. Il parametro type viene visualizzato solo come tipo di parametro del compare
metodo, quindi T
può essere passato in sicurezza ad esso. Ad esempio, se hai un DateComparator implements Comparator<java.util.Date> { ... }
e vuoi ordinare un List<java.sql.Date>
con quel comparatore ( java.sql.Date
è una sottoclasse di java.util.Date
), puoi fare con:
<T> void sort(List<T> what, Comparator<? super T> how)
ma non con
<T> void sort(List<T> what, Comparator<T> how)
Guarda il principio di sostituzione di Liskov . In effetti, se la classe B estende la classe A, dovresti essere in grado di usare una B ogni volta che è richiesta una A.
contra variant
dire. super.doSomething("String")
non può essere sostituito da sub.doSomething(Object)
.