Réponses:
Covariance:
class Super {
Object getSomething(){}
}
class Sub extends Super {
String getSomething() {}
}
Sub # getSomething est covariant car il renvoie une sous-classe du type de retour de Super # getSomething (mais remplit le contrat de Super.getSomething ())
Contravariance
class Super{
void doSomething(String parameter)
}
class Sub extends Super{
void doSomething(Object parameter)
}
Sub # doSomething est contravariant car il prend un paramètre d'une superclasse du paramètre de Super # doSomething (mais, encore une fois, remplit le contrat de Super # doSomething)
Remarque: cet exemple ne fonctionne pas en Java. Le compilateur Java surchargerait et ne remplacerait pas la méthode doSomething (). D'autres langages prennent en charge ce style de contravariance.
Génériques
Ceci est également possible pour les génériques:
List<String> aList...
List<? extends Object> covariantList = aList;
List<? super String> contravariantList = aList;
Vous pouvez maintenant accéder à toutes les méthodes covariantList
qui n'acceptent pas de paramètre générique (car il doit s'agir de quelque chose "étend Object"), mais les getters fonctionneront bien (car l'objet retourné sera toujours de type "Object")
Le contraire est vrai pour contravariantList
: Vous pouvez accéder à toutes les méthodes avec des paramètres génériques (vous savez qu'il doit s'agir d'une superclasse de "String", vous pouvez donc toujours en passer une), mais pas de getters (Le type renvoyé peut être de tout autre supertype de String )
Co-variance: Iterable et Iterator. Il est presque toujours judicieux de définir une co-variante Iterable
ou Iterator
. Iterator<? extends T>
peut être utilisé comme Iterator<T>
- le seul endroit où le paramètre de type apparaît est le type de retour de la next
méthode, afin qu'il puisse être converti en toute sécurité T
. Mais si vous avez des S
extensions T
, vous pouvez également attribuer Iterator<S>
une variable de type Iterator<? extends T>
. Par exemple, si vous définissez une méthode de recherche:
boolean find(Iterable<Object> where, Object what)
vous ne pourrez pas l'appeler avec List<Integer>
et 5
, il est donc mieux défini comme
boolean find(Iterable<?> where, Object what)
Contre-variance: comparateur. Il a presque toujours du sens à utiliser Comparator<? super T>
, car il peut être utilisé comme Comparator<T>
. Le paramètre type apparaît uniquement en tant que type de paramètre de compare
méthode, il T
peut donc lui être transmis en toute sécurité. Par exemple, si vous avez un DateComparator implements Comparator<java.util.Date> { ... }
et que vous souhaitez trier un List<java.sql.Date>
avec ce comparateur ( java.sql.Date
est une sous-classe de java.util.Date
), vous pouvez faire avec:
<T> void sort(List<T> what, Comparator<? super T> how)
mais pas avec
<T> void sort(List<T> what, Comparator<T> how)
Regardez le principe de substitution de Liskov . En effet, si la classe B étend la classe A, vous devriez pouvoir utiliser un B chaque fois qu'un A est requis.
contra variant
dire. super.doSomething("String")
n'a pas pu être remplacé par sub.doSomething(Object)
.