public class Test {
public class A {}
public class B extends A {}
public class C extends B {}
public void testCoVariance(List<? extends B> myBlist) {
B b = new B();
C c = new C();
myBlist.add(b); // does not compile
myBlist.add(c); // does not compile
A a = myBlist.get(0);
}
public void testContraVariance(List<? super B> myBlist) {
B b = new B();
C c = new C();
myBlist.add(b);
myBlist.add(c);
A a = myBlist.get(0); // does not compile
}
}
pecs(生产商) extends 消费者 super ) 记忆的→ 获取和放置原则。 该原则规定: 使用 extends 仅从结构中获取值时使用通配符。 使用 super 仅将值放入结构时使用通配符。 当你得到和得到的时候不要使用通配符。 java示例:
class Super {
Number testCoVariance() {
return null;
}
void testContraVariance(Number parameter) {
}
}
class Sub extends Super {
@Override
Integer testCoVariance() {
return null;
} //compiles successfully i.e. return type is don't care(Integer is subtype of Number)
@Override
void testContraVariance(Integer parameter) {
} //doesn't support even though Integer is subtype of Number
}
Object name= new String("prem"); //works
List<Number> numbers = new ArrayList<Integer>();//gets compile time error
Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
myNumber[0] = 3.14; //attempt of heap pollution i.e. at runtime gets java.lang.ArrayStoreException: java.lang.Double(we can fool compiler but not run-time)
List<String> list=new ArrayList<>();
list.add("prem");
List<Object> listObject=list; //Type mismatch: cannot convert from List<String> to List<Object> at Compiletime
更多示例
图像src 有界(即朝向某处)通配符:通配符有3种不同的风格: 差异/非差异: ? 或者 ? extends Object -无界通配符。它代表所有类型的家庭。当你得到和得到的时候使用。 协方差: ? extends T (属于 T )-具有上限的通配符。 T 是继承层次结构中最上层的类。使用 extends 仅从结构中获取值时使用通配符。 抵销差额: ? super T (所有类型的族,这些类型是 T )-具有下限的通配符。 T 是继承层次结构中最低级的类。使用 super 仅将值放入结构时使用通配符。 注:通配符 ? 表示零次或一次,表示未知类型。通配符可以用作参数的类型,决不能用作泛型方法调用、泛型类示例创建的类型参数(即,当使用通配符时,引用不会在程序中的其他地方使用,如我们使用的) T )
import java.util.ArrayList;
import java.util.List;
class Shape { void draw() {}}
class Circle extends Shape {void draw() {}}
class Square extends Shape {void draw() {}}
class Rectangle extends Shape {void draw() {}}
public class Test {
public static void main(String[] args) {
//? extends Shape i.e. can use any sub type of Shape, here Shape is Upper Bound in inheritance hierarchy
List<? extends Shape> intList5 = new ArrayList<Shape>();
List<? extends Shape> intList6 = new ArrayList<Cricle>();
List<? extends Shape> intList7 = new ArrayList<Rectangle>();
List<? extends Shape> intList9 = new ArrayList<Object>();//ERROR.
//? super Shape i.e. can use any super type of Shape, here Shape is Lower Bound in inheritance hierarchy
List<? super Shape> inList5 = new ArrayList<Shape>();
List<? super Shape> inList6 = new ArrayList<Object>();
List<? super Shape> inList7 = new ArrayList<Circle>(); //ERROR.
//-----------------------------------------------------------
Circle circle = new Circle();
Shape shape = circle; // OK. Circle IS-A Shape
List<Circle> circles = new ArrayList<>();
List<Shape> shapes = circles; // ERROR. List<Circle> is not subtype of List<Shape> even when Circle IS-A Shape
List<? extends Circle> circles2 = new ArrayList<>();
List<? extends Shape> shapes2 = circles2; // OK. List<? extends Circle> is subtype of List<? extends Shape>
//-----------------------------------------------------------
Shape shape2 = new Shape();
Circle circle2= (Circle) shape2; // OK. with type casting
List<Shape> shapes3 = new ArrayList<>();
List<Circle> circles3 = shapes3; //ERROR. List<Circle> is not subtype of List<Shape> even Circle is subetype of Shape
List<? super Shape> shapes4 = new ArrayList<>();
List<? super Circle> circles4 = shapes4; //OK.
}
/*
* Example for an upper bound wildcard (Get values i.e Producer `extends`)
*
* */
public void testCoVariance(List<? extends Shape> list) {
list.add(new Object());//ERROR
list.add(new Shape()); //ERROR
list.add(new Circle()); // ERROR
list.add(new Square()); // ERROR
list.add(new Rectangle()); // ERROR
Shape shape= list.get(0);//OK so list act as produces only
/*
* You can't add a Shape,Circle,Square,Rectangle to a List<? extends Shape>
* You can get an object and know that it will be an Shape
*/
}
/*
* Example for a lower bound wildcard (Put values i.e Consumer`super`)
* */
public void testContraVariance(List<? super Shape> list) {
list.add(new Object());//ERROR
list.add(new Shape());//OK
list.add(new Circle());//OK
list.add(new Square());//OK
list.add(new Rectangle());//OK
Shape shape= list.get(0); // ERROR. Type mismatch, so list acts only as consumer
Object object= list.get(0); //OK gets an object, but we don't know what kind of Object it is.
/*
* You can add a Shape,Circle,Square,Rectangle to a List<? super Shape>
* You can't get an Shape(but can get Object) and don't know what kind of Shape it is.
*/
}
}
正如我在回答另一个问题时所解释的,pecs是由josh bloch创建的一种帮助记忆制作者的助记设备 extends ,消费者 super . 这意味着当参数化类型被传递给方法时,将产生 T (它们将以某种方式从中被取回), ? extends T 应该使用,因为 T 也是一个 T . 当传递给方法的参数化类型将使用 T (他们将被传给它去做某事), ? super T 应使用,因为 T 可以合法地传递给任何接受 T . 一 Comparator<Number> 可以用在 Collection<Integer> ,例如。 ? extends T 不起作用,因为 Comparator<Integer> 无法在计算机上操作 Collection<Number> . 请注意,通常您应该只使用 ? extends T 以及 ? super T 对于某些方法的参数。方法应该只使用 T 作为泛型返回类型上的类型参数。
// Source
List<Integer> intList = Arrays.asList(1,2,3);
List<Double> doubleList = Arrays.asList(2.78,3.14);
List<Number> numList = Arrays.asList(1,2,2.78,3.14,5);
// Destination
List<Integer> intList2 = new ArrayList<>();
List<Double> doublesList2 = new ArrayList<>();
List<Number> numList2 = new ArrayList<>();
// Works
copyElements1(intList,intList2); // from int to int
copyElements1(doubleList,doublesList2); // from double to double
static <T> void copyElements1(Collection<T> src, Collection<T> dest) {
for(T n : src){
dest.add(n);
}
}
// Let's try to copy intList to its supertype
copyElements1(intList,numList2); // error, method signature just says "T"
// and here the compiler is given
// two types: Integer and Number,
// so which one shall it be?
// PECS to the rescue!
copyElements2(intList,numList2); // possible
// copy Integer (? extends T) to its supertype (Number is super of Integer)
private static <T> void copyElements2(Collection<? extends T> src,
Collection<? super T> dest) {
for(T n : src){
dest.add(n);
}
}
class Creature{}// X
class Animal extends Creature{}// Y
class Fish extends Animal{}// Z
class Shark extends Fish{}// A
class HammerSkark extends Shark{}// B
class DeadHammerShark extends HammerSkark{}// C
让我们澄清一下私募股权投资:
List<? extends Shark> sharks = new ArrayList<>();
为什么不能在此列表中添加扩展“shark”的对象?比如:
sharks.add(new HammerShark());//will result in compilation error
Object o;
o = sharks.get(2);// only assignment that works
Animal s;
s = sharks.get(2);//doen't work
在运行时,列表的类型可以是a:x,y,z。。。编译器可以编译赋值语句(这似乎是正确的),但在运行时,s(animal)的类型在层次结构中可能低于列表中声明的类型(可以是creater或更高)。这是不允许的。 总而言之 我们使用 <? super T> 添加类型等于或低于 T 到 List . 我们无法从中阅读。 我们使用 <? extends T> 读取类型等于或低于 T 从列表中。我们不能给它添加元素。
10条答案
按热度按时间eqzww0vc1#
lskq00tm2#
简而言之,记住PEC的三个简单规则:
使用
<? extends T>
通配符,如果需要检索类型为的对象T
从收藏中。使用
<? super T>
通配符,如果需要放置类型为T
在一个集合中。如果您需要同时满足这两个条件,那么,不要使用任何通配符。就这么简单。
kiz8lqtg3#
热释光;博士:“佩奇”是从收藏的Angular 。如果您只是从常规集合中提取项,那么它是一个生产者,您应该使用
extends
; 如果你只是在塞东西,它是一个消费者,你应该使用super
. 如果对同一个集合同时使用这两种方法,则不应使用任何一种方法extends
或者super
.假设您有一个方法,该方法将一组东西作为其参数,但您希望它比只接受
Collection<Thing>
.案例1:你想通过收集和做每件事。
那么列表是生产者,所以你应该使用
Collection<? extends Thing>
.理由是
Collection<? extends Thing>
可以容纳任何类型的Thing
,因此每个元素将作为Thing
当你做手术的时候(实际上,不能将任何内容(null除外)添加到Collection<? extends Thing>
,因为您无法在运行时知道Thing
收藏可以保存。)案例2:你想把东西添加到集合中。
那么这个列表就是一个消费者,所以你应该使用
Collection<? super Thing>
.这里的理由是
Collection<? extends Thing>
,Collection<? super Thing>
可以随时举行Thing
不管实际的参数化类型是什么。在这里,你不在乎列表中已经有什么,只要它允许Thing
待补充;这是什么? super Thing
保证。0dxa2lsx4#
pecs(生产商)
extends
消费者super
)记忆的→ 获取和放置原则。
该原则规定:
使用
extends
仅从结构中获取值时使用通配符。使用
super
仅将值放入结构时使用通配符。当你得到和得到的时候不要使用通配符。
java示例:
liskov替换原则(lsp)指出,“程序中的对象应该可以用其子类型的示例替换,而不改变该程序的正确性”。
在程序设计语言的类型系统中,一种类型规则
协变的,如果它保持类型的顺序(≤), 从更具体到更一般的排序类型;
相反,如果它颠倒了这个顺序;
不变的或非变化的,如果两者都不适用。
协方差与逆变
只读数据类型(源)可以是协变的;
只写数据类型(接收器)可以是反变的。
作为源和汇的可变数据类型应该是不变的。
为了说明这种普遍现象,请考虑数组类型。对于类型动物,我们可以制作类型动物[]
协变的:猫[]是动物[];
反变:动物[]是猫[];
不变量:动物[]不是猫[],猫[]不是动物[]。
java示例:
更多示例
图像src
有界(即朝向某处)通配符:通配符有3种不同的风格:
差异/非差异:
?
或者? extends Object
-无界通配符。它代表所有类型的家庭。当你得到和得到的时候使用。协方差:
? extends T
(属于T
)-具有上限的通配符。T
是继承层次结构中最上层的类。使用extends
仅从结构中获取值时使用通配符。抵销差额:
? super T
(所有类型的族,这些类型是T
)-具有下限的通配符。T
是继承层次结构中最低级的类。使用super
仅将值放入结构时使用通配符。注:通配符
?
表示零次或一次,表示未知类型。通配符可以用作参数的类型,决不能用作泛型方法调用、泛型类示例创建的类型参数(即,当使用通配符时,引用不会在程序中的其他地方使用,如我们使用的)T
)泛型和示例
协方差和逆方差根据类型确定兼容性。无论哪种情况,方差都是有向关系。协方差可以翻译为“同一方向上的不同”或“不同”,而逆方差则意味着“相反方向上的不同”或“相反方向上的不同”。协变类型和逆变类型不一样,但它们之间存在相关性。这些名字暗示了相互关系的方向。
gblwokeq5#
正如我在回答另一个问题时所解释的,pecs是由josh bloch创建的一种帮助记忆制作者的助记设备
extends
,消费者super
.这意味着当参数化类型被传递给方法时,将产生
T
(它们将以某种方式从中被取回),? extends T
应该使用,因为T
也是一个T
.当传递给方法的参数化类型将使用
T
(他们将被传给它去做某事),? super T
应使用,因为T
可以合法地传递给任何接受T
. 一Comparator<Number>
可以用在Collection<Integer>
,例如。? extends T
不起作用,因为Comparator<Integer>
无法在计算机上操作Collection<Number>
.请注意,通常您应该只使用
? extends T
以及? super T
对于某些方法的参数。方法应该只使用T
作为泛型返回类型上的类型参数。xsuvu9jc6#
(添加一个答案,因为从来没有足够的带有泛型通配符的示例)
svmlkihl7#
这是我思考extends与super的最清晰、最简单的方法:
extends
是用来读书的super
是用来写作的我发现“pecs”是一种不明显的方式来考虑谁是“生产者”谁是“消费者”“pecs”是从数据集合本身的Angular 定义的——如果对象被写入集合中,集合“消费”(它从调用代码中消费对象),如果对象被从集合中读取,集合“产生”(它从调用代码中产生对象)。但这与其他事物的命名方式相反。标准javaapi是从调用代码的Angular 命名的,而不是集合本身。例如,java.util.list的以集合为中心的视图应该有一个名为“receive()”的方法,而不是“add()”——毕竟,调用代码添加了元素,但是列表本身接收了元素。
我认为从与集合交互的代码的Angular 来思考问题更直观、自然和一致——代码是从集合“读取”还是“写入”集合?在此之后,写入集合的任何代码都将是“生产者”,从集合中读取的任何代码都将是“消费者”。
ssm49v7z8#
pecs“规则”只是确保以下内容是合法的:
消费者:随便
?
是的,它在法律上可以指T
制片人:随便?
是的,它可以合法地被T
典型的配对List<? extends T> producer, List<? super T> consumer
只是确保编译器可以执行标准的“is-a”继承关系规则。如果我们能合法地这么做,说起来可能更简单<T extends ?>, <? extends T>
(或者更好的是在scala中,正如您在上面看到的,它是Collection<Thing>
q7solyqu9#
假设这个层次结构:
让我们澄清一下私募股权投资:
为什么不能在此列表中添加扩展“shark”的对象?比如:
因为在运行时有一个可以是a、b或c类型的列表,所以不能在其中添加任何a、b或c类型的对象,因为最终会出现java不允许的组合。
实际上,编译器确实可以在compiletime看到您添加了一个b:
…但它无法判断在运行时,您的b是列表类型的子类型还是超类型。在运行时,列表类型可以是a、b、c中的任何一种类型。例如,你不能在死锤鲨列表中添加hammerskark(超级类型)。
让我们澄清cs-超级消费者:
在相同的层次结构中,我们可以尝试以下方法:
您可以添加什么以及为什么添加到此列表中?
您可以添加上述类型的对象,因为shark(a,b,c)下面的任何对象都将始终是shark(x,y,z)上面的任何对象的子类型。容易理解。
不能在shark之上添加类型,因为在运行时,添加的对象的类型在层次结构中可以高于列表的声明类型(x,y,z)。这是不允许的。
但是为什么你不能从这个列表中阅读呢(我的意思是你可以从中得到一个元素,但是你不能把它赋给对象o以外的任何东西
在运行时,列表的类型可以是a:x,y,z。。。编译器可以编译赋值语句(这似乎是正确的),但在运行时,s(animal)的类型在层次结构中可能低于列表中声明的类型(可以是creater或更高)。这是不允许的。
总而言之
我们使用
<? super T>
添加类型等于或低于T
到List
. 我们无法从中阅读。我们使用
<? extends T>
读取类型等于或低于T
从列表中。我们不能给它添加元素。dojqjjoe10#
在计算机科学中,这背后的原理被称为
协方差:
? extends MyClass
,相反:
? super MyClass
和不变性/非方差:
MyClass
下面的图片应该解释这个概念。图片提供:andrey tyukin