Pour comprendre la covariance et la contravariance, il faut d'abord introduire :
D'après le principe de substitution de Liskov, si C est une sous-classe de P, alors P peut remplacer C, c'est-à-dire P p = new C();
C hérite de P, enregistré comme P
Si F est inchangé, lorsque CSauf par exemple Integer est une sous-classe de Number Selon le principe de substitution de Liskov
Number number = new Integer(1); //correct
List<Number> list = new ArrayList<Integer>(1); //error
List<Number> et <code>List<Integer>
sans aucun héritage. relation# 🎜🎜#Qu'est-ce que la covarianceList<Number>
和List<Integer>
不存在任何继承关系
如果F是协变的,当 C <= P 时,那么 F(C) <= F(P)
Java 提供了一个extends来将不变转为协变,例如:
List<? extends Number> list = new ArrayList<Integer>(1); //corrent
此时的List<? extends Number>
可以看作为ArrayList<Integer>
的父类
? extend Number
可以看作为一个类型范围,表示Number的某一个子类
数组默认是协变的
Number[] numbers = new Integer[3];
如果F是逆变的,当 C <= P 时,那么 F(C) >= F(P)
Java 提供了一个super来将不变转为协变,例如:
List<? super Number> list = new ArrayList<Object>(1); //corrent
此时的 List<? super Number>
可以看作为 ArrayList<Object>
的父类
首先,我们看看Collection.add的实现:
public interface List<E> extends Collection<E> { boolean add(E e); }
下面代码将会报错?? extends Number
与Integer
类型不匹配
List<? extends Number> list = new ArrayList<Integer>(); // correct list.add(Integer.valueOf(1)); //error
首先在调用add方法时,泛型E
自动变成了<? extends Number>
第二行报错,也就是说? extends Number
不是Integer
的父类。这里要将 List<? extends Number>
是ArrayList<Integer>
的父类区分开。
? extends Number
可以看作为一个类型范围中某一个类型,表示Number的某一个子类,但又没明确是哪个子类,可能是Float,可能是Short,也可能是Integer的子类(Integer被final修饰,不可能有子类,这里只是一种假设情况),它只确定了它的上界为 Number,并没有确定下界(有可能存在? extends Number
< Integer
),因此 ? extends Number
不是Integer
的父类
将上面代码稍做修改就正确了:
List<? super Number> list = new ArrayList<Object>(); // correct list.add(Integer.valueOf(1)); //correct
首先因为逆变,List<? super Number>
是ArrayList<Object>
的父类,第一行正确。
第二行: ? super Number
是Integer
的父类,原因是:? super Number
表示Number的某一个父类,可能是Serializable
也可能是 Object
Java fournit une extension pour convertir l'invariance en covariance, par exemple :
public static <T> void copy(List<? super T> dest, List<? extends T> src) { int srcSize = src.size(); if (srcSize > dest.size()) throw new IndexOutOfBoundsException("Source does not fit in dest"); if (srcSize < COPY_THRESHOLD || (src instanceof RandomAccess && dest instanceof RandomAccess)) { for (int i=0; i<srcSize; i++) dest.set(i, src.get(i)); } else { ListIterator<? super T> di=dest.listIterator(); ListIterator<? extends T> si=src.listIterator(); for (int i=0; i<srcSize; i++) { di.next(); di.set(si.next()); } } }
List<? extends Number>
peut être considéré comme la classe parent de ArrayList<Integer>
extend Number
peut être considéré comme un type ? plage, représentant une certaine sous-classe de Nombre
private static <E> E getFirst(List<? extends E> list){ return list.get(0); } private static <E> void setFirst(List<? super E> list, E firstElement){ list.add(firstElement); } public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(); setFirst(list, 1); Number number = getFirst(list); }
Qu'est-ce que la contravariance
List<? super Number>
peut être considérée comme la classe parent de ArrayList<Object>
#🎜🎜##🎜🎜#extends et super#🎜🎜##🎜🎜##🎜🎜#Tout d'abord, regardons l'implémentation de Collection.add : #🎜🎜##🎜🎜#rrreee#🎜🎜## 🎜🎜 #Le code suivant signalera-t-il une erreur ? #🎜🎜#? extends Number
ne correspond pas au type Integer
#🎜🎜#rrreee#🎜🎜#Tout d'abord, lors de l'appel de la méthode add, le E
code> devient automatiquement <? extends Number>
#🎜🎜##🎜🎜#La deuxième ligne signale une erreur, ce qui signifie que
? classe parente. Ici, nous devons distinguer que List<? extends Number>
est la classe parent de ArrayList<Integer>
. #🎜🎜##🎜🎜#? extends Number
peut être considéré comme un certain type dans une plage de types, représentant une certaine sous-classe de Number, mais il n'est pas clair de quelle sous-classe il s'agit, peut-être Float. Il peut s'agir de Short ou d'une sous-classe d'Integer (Integer est modifié par final et ne peut pas avoir de sous-classes. Il s'agit simplement d'une situation hypothétique. Il détermine uniquement sa limite supérieure en tant que nombre et ne détermine pas la limite inférieure (cela peut). existe ? extends Number
< Integer
), donc ? extends Number
n'est pas la classe parent de Integer
#🎜 🎜## 🎜🎜##🎜🎜# Modifiez légèrement le code ci-dessus et il sera correct : #🎜🎜##🎜🎜#rrreee#🎜🎜#Tout d'abord, à cause de l'inversion, List<? ;
est ArrayList<Object>, la première ligne est correcte. #🎜🎜##🎜🎜#La deuxième ligne : ? super Number
est la classe parent de Integer
, la raison est : super Number
signifie Number One des classes parent peut être Serializing
ou Object
, mais peu importe de laquelle il s'agit, la classe parent de Number doit être la classe parent d'Integer, donc le la deuxième ligne est également correcte# 🎜🎜##🎜🎜#Devrions-nous utiliser extends ou super ? ##🎜🎜##🎜🎜##🎜🎜#Lorsque vous souhaitez écrire des données dans une classe générique, utilisez super #🎜🎜##🎜🎜##🎜🎜##🎜🎜#Si vous souhaitez à la fois obtenir et écrivez, vous n'avez pas besoin de caractères génériques (c'est-à-dire que ni extends ni super ne sont utilisés) #🎜🎜##🎜🎜##🎜🎜#rrreeeCe qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!