当传递给方法的参数化类型将消费T的实例(它们将被传递给它做某事)时,应该使用? super T,因为T的实例可以合法地传递给任何接受T的超类型的方法。例如,Comparator<Number>可以在Collection<Integer>上使用。? extends T不起作用,因为Comparator<Integer>不能在Collection<Number>上操作。
请注意,通常您应该只对某些方法的参数使用? extends T和? super T。方法应该只使用T作为泛型返回类型的类型参数。
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 compilemyBlist.add(c); // does not compileA 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}}
class Super {Number testCoVariance() {return null;}void testContraVariance(Number parameter) {}}
class Sub extends Super {@OverrideInteger testCoVariance() {return null;} //compiles successfully i.e. return type is don't care(Integer is subtype of Number)@Overridevoid testContraVariance(Integer parameter) {} //doesn't support even though Integer is subtype of Number}
Object name= new String("prem"); //worksList<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
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 hierarchyList<? 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 hierarchyList<? 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());//ERRORlist.add(new Shape()); //ERRORlist.add(new Circle()); // ERRORlist.add(new Square()); // ERRORlist.add(new Rectangle()); // ERRORShape 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());//ERRORlist.add(new Shape());//OKlist.add(new Circle());//OKlist.add(new Square());//OKlist.add(new Rectangle());//OKShape shape= list.get(0); // ERROR. Type mismatch, so list acts only as consumerObject 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.*/}}
// SourceList<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);
// DestinationList<Integer> intList2 = new ArrayList<>();List<Double> doublesList2 = new ArrayList<>();List<Number> numList2 = new ArrayList<>();
// WorkscopyElements1(intList,intList2); // from int to intcopyElements1(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 supertypecopyElements1(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);}}
// copies the elements of 'producer' into 'consumer'static <T> void copy(List<? extends T> producer, List<? super T> consumer) {for(T t : producer)consumer.add(t);}
用赋值类比来说明这一点,对于consumer,?通配符(未知类型)是引用-赋值的“左手边”-<? super T>确保无论?是什么,T“IS-A”?-T都可以赋值给它,因为?是一个超级类型(或者最多是与T相同的类型)。
public class Collections {public static <T> void copy(List<? super T> dest, List<? extends T> src) {for (int i = 0; i < src.size(); i++)dest.set(i, src.get(i));}}
如果我们使用像List<? super Animal>这样的超级类型,我们不知道List<T>的确切类型。它可能是List<Animal>或List<BaseAnimal>或List<Object>的列表。我们无法知道。这意味着我们永远无法从这个列表中获取值,因为我们不知道该类型是什么。然而,我们可以放入任何Animal的数据类型或将其扩展到List。因为我们只能将数据放入List,所以它被称为数据消费者。
import java.util.ArrayList;import java.util.List;
public class Generics {public static void main(String[] args) {
Generics generics = new Generics();
generics.producerExtends(new ArrayList<Duck>());generics.producerExtends(new ArrayList<Animal>());
generics.consumerSuper(new ArrayList<Object>());generics.consumerSuper(new ArrayList<Animal>());
}
// ? extends T is an upper boundpublic void producerExtends (List<? extends Animal> list) {
// Following are illegal since we never know exactly what type the list will be// list.add(new Duck());// list.add(new Animal());
// We can read from it since we are always getting an Animal or subclass from it// However we can read them as an animal type, so this compiles fineif (list.size() > 0) {Animal animal = list.get(0);}}
// ? extends T is a lower boundpublic void consumerSuper (List<? super Animal> list) {// It will be either a list of Animal or a superclass of it// Therefore we can add any type which extends animalslist.add(new Duck());list.add(new Animal());
// Compiler won't allow this it could potentially be a super type of Animal// Animal animal = list.get(0);}