Veuillez montrer un bon exemple de covariance et de contravariance en Java.
la source
Veuillez montrer un bon exemple de covariance et de contravariance en Java.
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
ouIterator
.Iterator<? extends T>
peut être utilisé commeIterator<T>
- le seul endroit où le paramètre de type apparaît est le type de retour de lanext
méthode, afin qu'il puisse être converti en toute sécuritéT
. Mais si vous avez desS
extensionsT
, vous pouvez également attribuerIterator<S>
une variable de typeIterator<? extends T>
. Par exemple, si vous définissez une méthode de recherche:vous ne pourrez pas l'appeler avec
List<Integer>
et5
, il est donc mieux défini commeContre-variance: comparateur. Il a presque toujours du sens à utiliser
Comparator<? super T>
, car il peut être utilisé commeComparator<T>
. Le paramètre type apparaît uniquement en tant que type de paramètre decompare
méthode, ilT
peut donc lui être transmis en toute sécurité. Par exemple, si vous avez unDateComparator implements Comparator<java.util.Date> { ... }
et que vous souhaitez trier unList<java.sql.Date>
avec ce comparateur (java.sql.Date
est une sous-classe dejava.util.Date
), vous pouvez faire avec:mais pas avec
la source
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.
la source
contra variant
dire.super.doSomething("String")
n'a pas pu être remplacé parsub.doSomething(Object)
.