Difference between <? super T> and <? extends T> in Java
What is the difference between List<? super T>
List<? super T>
and List<? extends T>
List<? extends T>
?
I used to use List<? extends T>
List<? extends T>
, but it does not allow me to add elements to it list.add(e)
, whereas the List<? super T>
List<? super T>
does.
extends
The wildcard declaration of List<? extends Number> foo3
List<? extends Number> foo3
means that any of these are legal assignments:
List<? extends Number> foo3 = new ArrayList<Number>(); // Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>(); // Double extends Number
Reading - Given the above possible assignments, what type of object are you guaranteed to read from List foo3
:
Number
because any of the lists that could be assigned to foo3
contain a Number
or a subclass of Number
. Integer
because foo3
could be pointing at a List<Double>
. Double
because foo3
could be pointing at a List<Integer>
. Writing - Given the above possible assignments, what type of object could you add to List foo3
that would be legal for all the above possible ArrayList
assignments:
Integer
because foo3
could be pointing at a List<Double>
. Double
because foo3
could be pointing at a List<Integer>
. Number
because foo3
could be pointing at a List<Integer>
. You can't add any object to List<? extends T>
List<? extends T>
because you can't guarantee what kind of List
it is really pointing to, so you can't guarantee that the object is allowed in that List
. The only "guarantee" is that you can only read from it and you'll get a T
or subclass of T
.
super
Now consider List <? super T>
List <? super T>
.
The wildcard declaration of List<? super Integer> foo3
List<? super Integer> foo3
means that any of these are legal assignments:
List<? super Integer> foo3 = new ArrayList<Integer>(); // Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Number>(); // Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>(); // Object is a superclass of Integer
Reading - Given the above possible assignments, what type of object are you guaranteed to receive when you read from List foo3
:
Integer
because foo3
could be pointing at a List<Number>
or List<Object>
. Number
because foo3
could be pointing at a List<Object>
. Object
or subclass of Object
(but you don't know what subclass). Writing - Given the above possible assignments, what type of object could you add to List foo3
that would be legal for all the above possible ArrayList
assignments:
Integer
because an Integer
is allowed in any of above lists. Integer
because an instance of a subclass of Integer
is allowed in any of the above lists. Double
because foo3
could be pointing at an ArrayList<Integer>
. Number
because foo3
could be pointing at an ArrayList<Integer>
. Object
because foo3
could be pointing at an ArrayList<Integer>
. PECS
Remember PECS: "Producer Extends, Consumer Super" .
"Producer Extends" - If you need a List
to produce T
values (you want to read T
s from the list), you need to declare it with ? extends T
? extends T
, eg List<? extends Integer>
List<? extends Integer>
. But you cannot add to this list.
"Consumer Super" - If you need a List
to consume T
values (you want to write T
s into the list), you need to declare it with ? super T
? super T
, eg List<? super Integer>
List<? super Integer>
. But there are no guarantees what type of object you may read from this list.
If you need to both read from and write to a list, you need to declare it exactly with no wildcards, eg List<Integer>
.
Example
Note this example from the Java Generics FAQ. Note how the source list src
(the producing list) uses extends
, and the destination list dest
(the consuming list) uses super
:
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));
}
}
Also see How can I add to List<? extends Number> data structures?
Imagine having this hierarchy
1. Extends
By writing
List<? extends C2> list;
you are saying that list
will be able to reference an object of type (for example) ArrayList
whose generic type is one of the 7 subtypes of C2
( C2
included):
new ArrayList<C2>();
, (an object that can store C2 or subtypes) or new ArrayList<D1>();
, (an object that can store D1 or subtypes) or new ArrayList<D2>();
, (an object that can store D2 or subtypes) or... and so on. Seven different cases:
1) new ArrayList<C2>(): can store C2 D1 D2 E1 E2 E3 E4
2) new ArrayList<D1>(): can store D1 E1 E2
3) new ArrayList<D2>(): can store D2 E3 E4
4) new ArrayList<E1>(): can store E1
5) new ArrayList<E2>(): can store E2
6) new ArrayList<E3>(): can store E3
7) new ArrayList<E4>(): can store E4
We have a set of "storable" types for each possible case: 7 (red) sets here graphically represented
As you can see, there is not a safe type that is common to every case:
list.add(new C2(){});
because it could be list = new ArrayList<D1>();
list.add(new D1(){});
because it could be list = new ArrayList<D2>();
and so on.
2. Super
By writing
List<? super C2> list;
you are saying that list
will be able to reference an object of type (for example) ArrayList
whose generic type is one of the 7 supertypes of C2
( C2
included):
new ArrayList<A1>();
, (an object that can store A1 or subtypes) or new ArrayList<A2>();
, (an object that can store A2 or subtypes) or new ArrayList<A3>();
, (an object that can store A3 or subtypes) or... and so on. Seven different cases:
1) new ArrayList<A1>(): can store A1 B1 B2 C1 C2 D1 D2 E1 E2 E3 E4
2) new ArrayList<A2>(): can store A2 B2 C1 C2 D1 D2 E1 E2 E3 E4
3) new ArrayList<A3>(): can store A3 B3 C2 C3 D1 D2 E1 E2 E3 E4
4) new ArrayList<A4>(): can store A4 B3 B4 C2 C3 D1 D2 E1 E2 E3 E4
5) new ArrayList<B2>(): can store B2 C1 C2 D1 D2 E1 E2 E3 E4
6) new ArrayList<B3>(): can store B3 C2 C3 D1 D2 E1 E2 E3 E4
7) new ArrayList<C2>(): can store C2 D1 D2 E1 E2 E3 E4
We have a set of "storable" types for each possible case: 7 (red) sets here graphically represented
As you can see, here we have seven safe types that are common to every case: C2
, D1
, D2
, E1
, E2
, E3
, E4
.
list.add(new C2(){});
because, regardless of the kind of List we're referencing, C2
is allowed list.add(new D1(){});
because, regardless of the kind of List we're referencing, D1
is allowed and so on. You probably noticed that these types correspond to the hierarchy starting from type C2
.
Notes
Here the complete hierarchy if you wish to make some tests
interface A1{}
interface A2{}
interface A3{}
interface A4{}
interface B1 extends A1{}
interface B2 extends A1,A2{}
interface B3 extends A3,A4{}
interface B4 extends A4{}
interface C1 extends B2{}
interface C2 extends B2,B3{}
interface C3 extends B3{}
interface D1 extends C1,C2{}
interface D2 extends C2{}
interface E1 extends D1{}
interface E2 extends D1{}
interface E3 extends D2{}
interface E4 extends D2{}
I love the answer from @Bert F but this is the way my brain sees it.
I have an X in my hand. If I want to write my X into a List, that List needs to be either a List of X or a List of things that my X can be upcast to as I write them in ie any superclass of X...
List<? super X>
If I get a List and I want to read an X out of that List, that better be a List of X or a List of things that can be upcast to X as I read them out, ie anything that extends X
List<? extends X>
Hope this helps.
链接地址: http://www.djcxy.com/p/91976.html上一篇: Java:私有内部类合成构造函数