Donner des exemples de fonctions qui démontrent la covariance et la contravariance dans les cas de surcharge et de surcharge en Java? [fermé]

105

Veuillez montrer un bon exemple de covariance et de contravariance en Java.

JavaUser
la source

Réponses:

155

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 covariantListqui 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 )

Codé en dur
la source
79
Le premier exemple de contravariance ne fonctionne pas en Java. doSomething () dans la classe Sub est une surcharge, pas un remplacement.
Craig P. Motlin
15
En effet. Java ne prend pas en charge 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).
the_dark_destructor
Très bonne réponse. La covariance me semble logique. Mais pourriez-vous m'indiquer un paragraphe dans JLS qui décrit la contravariance? Pourquoi Sub.doSomething est invoqué?
Mikhail
2
Comme Craig l'a souligné, ce n'est pas le cas. Je pense qu'il y a un conflit entre le remplacement et la surcharge et SUN a choisi (comme toujours) l'option rétrocompatible. Ainsi, en Java, vous ne pouvez pas utiliser de paramètres contravariants lors de la substitution d'une méthode.
codé en dur le
1
Vous serez gentil de savoir pourquoi je reçois des votes négatifs pour ma réponse.
Codé en dur
48

Co-variance: Iterable et Iterator. Il est presque toujours judicieux de définir une co-variante Iterableou 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 nextméthode, afin qu'il puisse être converti en toute sécurité T. Mais si vous avez des Sextensions 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 compareméthode, il Tpeut 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.Dateest 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)
Yardena
la source
-4

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.

extraneon
la source
6
Cela ne répond pas à la question et est trompeur. Il serait tout à fait possible de concevoir un système variant qui rompt la correction sémantique et viole donc LSP.
Matt Whipple
ce n'est pas le cas pour contra variantdire. super.doSomething("String")n'a pas pu être remplacé par sub.doSomething(Object).
zinking le
Ce n'est pas la question
OlivierTerrien