2010-03-23 23 views

Répondre

119

Covariance:

class Super { 
    Object getSomething(){} 
} 
class Sub extends Super { 
    String getSomething() {} 
} 

Sub # getSomething est covariant parce qu'il renvoie une sous-classe du type de retour de Super # getSomething (mais fullfills le contrat de Super.getSomething())

contravariance

class Super{ 
    void doSomething(String parameter) 
} 
class Sub extends Super{ 
    void doSomething(Object parameter) 
} 

Sub # doSomething est contrava riant car il prend un paramètre d'une superclasse du paramètre de Super # doSomething (mais remplit à nouveau 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 langues supportent ce style de contravariance.

Generics

Ceci est également possible Génériques:

List<String> aList... 
List<? extends Object> covariantList = aList; 
List<? super String> contravariantList = aList; 

Vous pouvez désormais accéder à toutes les méthodes de covariantList qui ne prend pas un paramètre générique (comme il doit être quelque chose "extends Object"), mais les getters fonctionneront correctement (car l'objet retourné sera toujours de type "Object")

L'inverse 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", donc vous pouvez toujours en passer un), mais pas de getters (Le type retourné peut être de n'importe quel autre type de String)

+68

Le premier exemple de contravariance ne fonctionne pas en Java. doSomething() dans la classe Sub est une surcharge, pas un override. –

+13

En effet. Java ne supporte pas les arguments contravariants dans le sous-typage. Seule la covariance pour ce qui concerne les types de retour de méthode (comme dans le premier exemple). –

+0

Bonne réponse. La covariance me semble logique. Mais pourriez-vous me signaler un paragraphe dans JLS qui décrit la contravariance? Pourquoi Sub.doSomething est appelé? – Mikhail

1

Regardez le Liskov substitution principle. En effet, si la classe B étend la classe A, vous devriez être capable d'utiliser un B chaque fois qu'un A est requis.

+2

Cette ne répond pas à la question et est trompeur. Il serait tout à fait possible de concevoir un système variant qui brise la correction sémantique et viole donc le LSP. –

+0

ce n'est pas le cas pour dire «contra variant». 'super.doSomething (" String ")' n'a pas pu être remplacé par 'sub.doSomething (Object)'. – zinking

38

Co-variance: Iterable et Iterator. Il est presque toujours logique 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 méthode next, donc il peut être jeté en toute sécurité à T. Mais si vous avez S étend T, vous pouvez également affecter 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 serez pas en mesure d'appeler avec List<Integer> et 5, donc il vaut mieux défini comme

boolean find(Iterable<?> where, Object what) 

Contre-variance: Comparator. Il est presque toujours logique d'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 méthode compare. Par conséquent, T peut lui être transmis en toute sécurité.Par exemple, si vous avez un DateComparator implements Comparator<java.util.Date> { ... } et que vous voulez trier un List<java.sql.Date> avec ce comparateur (java.sql.Date est une sous-classe de java.util.Date), vous pouvez le faire avec:

<T> void sort(List<T> what, Comparator<? super T> how) 

mais pas avec

<T> void sort(List<T> what, Comparator<T> how)