Could anyone please let me understand why this piece of code is getting compiler error:
List<? super A> superList = new ArrayList<>();
superList.add(new Object());
While this one is perfectly correct:
setSuper(new ArrayList<Object>());
public void superMethod(List<? super A> list) {
//...
}
I understand that lower bound narrows available types to A or their descendants, but why this is fine for compiler when it comes to methods arguments?
An ArrayList<Object> is a valid value of type List<? super A>.
An ArrayList<A> is also a valid value of type List<? super A>.
But you can't add new Object() to an ArrayList<A>. So not all List<? super A> values can add an object, so the compiler correctly errors.
This question already has answers here:
What is PECS (Producer Extends Consumer Super)?
(16 answers)
Closed 4 years ago.
What is the difference between List<? super T> and List<? extends T> ?
I used to use List<? extends T>, but it does not allow me to add elements to it list.add(e), whereas the List<? super T> does.
extends
The wildcard declaration of 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:
You can read a Number because any of the lists that could be assigned to foo3 contain a Number or a subclass of Number.
You can't read an Integer because foo3 could be pointing at a List<Double>.
You can't read a 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:
You can't add an Integer because foo3 could be pointing at a List<Double>.
You can't add a Double because foo3 could be pointing at a List<Integer>.
You can't add a Number because foo3 could be pointing at a List<Integer>.
You can't add any object to 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>.
The wildcard declaration of 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:
You aren't guaranteed an Integer because foo3 could be pointing at a List<Number> or List<Object>.
You aren't guaranteed a Number because foo3 could be pointing at a List<Object>.
The only guarantee is that you will get an instance of an 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:
You can add an Integer because an Integer is allowed in any of above lists.
You can add an instance of a subclass of Integer because an instance of a subclass of Integer is allowed in any of the above lists.
You can't add a Double because foo3 could be pointing at an ArrayList<Integer>.
You can't add a Number because foo3 could be pointing at an ArrayList<Integer>.
You can't add an 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 Ts from the list), you need to declare it with ? extends T, e.g. 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 Ts into the list), you need to declare it with ? super T, e.g. 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, e.g. 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):
C2: new ArrayList<C2>();, (an object that can store C2 or subtypes) or
D1: new ArrayList<D1>();, (an object that can store D1 or subtypes) or
D2: 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:
you cannot list.add(new C2(){}); because it could be list = new ArrayList<D1>();
you cannot 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):
A1: new ArrayList<A1>();, (an object that can store A1 or subtypes) or
A2: new ArrayList<A2>();, (an object that can store A2 or subtypes) or
A3: 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.
you can list.add(new C2(){}); because, regardless of the kind of List we're referencing, C2 is allowed
you can 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 i.e. 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, i.e. anything that extends X
List<? extends X>
I'd like to visualize the difference. Suppose we have:
class A { }
class B extends A { }
class C extends B { }
List<? extends T> - reading and assigning:
|-------------------------|-------------------|---------------------------------|
| wildcard | get | assign |
|-------------------------|-------------------|---------------------------------|
| List<? extends C> | A B C | List<C> |
|-------------------------|-------------------|---------------------------------|
| List<? extends B> | A B | List<B> List<C> |
|-------------------------|-------------------|---------------------------------|
| List<? extends A> | A | List<A> List<B> List<C> |
|-------------------------|-------------------|---------------------------------|
List<? super T> - writing and assigning:
|-------------------------|-------------------|-------------------------------------------|
| wildcard | add | assign |
|-------------------------|-------------------|-------------------------------------------|
| List<? super C> | C | List<Object> List<A> List<B> List<C> |
|-------------------------|-------------------|-------------------------------------------|
| List<? super B> | B C | List<Object> List<A> List<B> |
|-------------------------|-------------------|-------------------------------------------|
| List<? super A> | A B C | List<Object> List<A> |
|-------------------------|-------------------|-------------------------------------------|
In all of the cases:
you can always get Object from a list regardless of the wildcard.
you can always add null to a mutable list regardless of the wildcard.
Based on Bert F's answer I would like to explain my understanding.
Lets say we have 3 classes as
public class Fruit{}
public class Melon extends Fruit{}
public class WaterMelon extends Melon{}
Here We have
List<? extends Fruit> fruitExtendedList = …
//Says that I can be a list of any object as long as this object extends Fruit.
Ok now lets try to get some value from fruitExtendedList
Fruit fruit = fruitExtendedList.get(position)
//This is valid as it can only return Fruit or its subclass.
Again lets try
Melon melon = fruitExtendedList.get(position)
//This is not valid because fruitExtendedList can be a list of Fruit only, it may not be
//list of Melon or WaterMelon and in java we cannot assign sub class object to
//super class object reference without explicitly casting it.
Same is the case for
WaterMelon waterMelon = fruitExtendedList.get(position)
Now lets try to set some object in fruitExtendedList
Adding fruit object
fruitExtendedList.add(new Fruit())
//This in not valid because as we know fruitExtendedList can be a list of any
//object as long as this object extends Fruit. So what if it was the list of
//WaterMelon or Melon you cannot add Fruit to the list of WaterMelon or Melon.
Adding Melon object
fruitExtendedList.add(new Melon())
//This would be valid if fruitExtendedList was the list of Fruit but it may
//not be, as it can also be the list of WaterMelon object. So, we see an invalid
//condition already.
Finally let try to add WaterMelon object
fruitExtendedList.add(new WaterMelon())
//Ok, we got it now we can finally write to fruitExtendedList as WaterMelon
//can be added to the list of Fruit or Melon as any superclass reference can point
//to its subclass object.
But wait what if someone decides to make a new type of Lemon lets say for arguments sake SaltyLemon as
public class SaltyLemon extends Lemon{}
Now fruitExtendedList can be list of Fruit, Melon, WaterMelon or SaltyLemon.
So, our statement
fruitExtendedList.add(new WaterMelon())
is not valid either.
Basically we can say that we cannot write anything to a fruitExtendedList.
This sums up List<? extends Fruit>
Now lets see
List<? super Melon> melonSuperList= …
//Says that I can be a list of anything as long as its object has super class of Melon.
Now lets try to get some value from melonSuperList
Fruit fruit = melonSuperList.get(position)
//This is not valid as melonSuperList can be a list of Object as in java all
//the object extends from Object class. So, Object can be super class of Melon and
//melonSuperList can be a list of Object type
Similarly Melon, WaterMelon or any other object cannot be read.
But note that we can read Object type instances
Object myObject = melonSuperList.get(position)
//This is valid because Object cannot have any super class and above statement
//can return only Fruit, Melon, WaterMelon or Object they all can be referenced by
//Object type reference.
Now, lets try to set some value from melonSuperList.
Adding Object type object
melonSuperList.add(new Object())
//This is not valid as melonSuperList can be a list of Fruit or Melon.
//Note that Melon itself can be considered as super class of Melon.
Adding Fruit type object
melonSuperList.add(new Fruit())
//This is also not valid as melonSuperList can be list of Melon
Adding Melon type object
melonSuperList.add(new Melon())
//This is valid because melonSuperList can be list of Object, Fruit or Melon and in
//this entire list we can add Melon type object.
Adding WaterMelon type object
melonSuperList.add(new WaterMelon())
//This is also valid because of same reason as adding Melon
To sum it up we can add Melon or its subclass in melonSuperList and read only Object type object.
super is a lower bound, and extends is an upper bound.
According to http://download.oracle.com/javase/tutorial/extra/generics/morefun.html :
The solution is to use a form of
bounded wildcard we haven't seen yet:
wildcards with a lower bound. The
syntax ? super T denotes an unknown
type that is a supertype of T (or T
itself; remember that the supertype
relation is reflexive). It is the dual
of the bounded wildcards we've been
using, where we use ? extends T to
denote an unknown type that is a
subtype of T.
Adding an item to the list:
List< ? extends X > doesn't allow to add anything, except for null into the list.
List< ? super X > allows to add anything that is-a X (X or its subtype), or null.
Getting an item from the list:
When you get an item from List< ? extends X >, you can assign it to a variable of type X or any supertype of X, including Object.
When you get an item from List< ? super X >, you can only assign it to a variable of type Object.
Some examples:
List<? extends Number> list1 = new ArrayList<Integer>();
list1.add(null); //OK
Number n = list1.get(0); //OK
Serializable s = list1.get(0); //OK
Object o = list1.get(0); //OK
list1.add(2.3); //ERROR
list1.add(5); //ERROR
list1.add(new Object()); //ERROR
Integer i = list1.get(0); //ERROR
List<? super Number> list2 = new ArrayList<Number>();
list2.add(null); //OK
list2.add(2.3); //OK
list2.add(5); //OK
Object o = list2.get(0); //OK
list2.add(new Object()); //ERROR
Number n = list2.get(0); //ERROR
Serializable s = list2.get(0); //ERROR
Integer i = list2.get(0); //ERROR
The up voted answers covers the details on many aspects. However, I would try to answer this in different way.
There are 2 things we need to consider,
1. Assignment to the list variable
List<? extends X> listvar;
Here, any list of X or list of subclasses of X can be assigned to listvar.
List<? extends Number> listvar;
listvar = new ArrayList<Number>();
listvar = new ArrayList<Integer>();
List<? super X> listvar;
Here, any list of X or list of superclasses of X can be assigned to listvar.
List<? super Number> listvar;
listvar = new ArrayList<Number>();
listvar = new ArrayList<Object>();
2. Perform Read or Write operation on the list variable
`List<? extends X> listvar;`
You can use this feature to accept a list in method arguments and perform any operations on type X (Note: You can only read objects of type X from the list).
`List<? super Number> listvar;
You can use this feature to accept a list in method arguments and perform any operations on type Object as You can only read objects of type Object from the list. But yes, additional thing here is , you can add objects of type X into the list.
You can go through all the answers above to understand why the .add() is restricted to '<?>', '<? extends>', and partly to '<? super>'.
But here's the conclusion of it all if you want to remember it, and dont want to go exploring the answer every time:
List<? extends A> means this will accept any List of A and subclass of A.
But you cannot add anything to this list. Not even objects of type A.
List<? super A> means this will accept any list of A and superclass of A.
You can add objects of type A and its subclasses.
The most confusing thing here is that whatever type restrictions we specify, assignment works only one way:
baseClassInstance = derivedClassInstance;
You may think that Integer extends Number and that an Integer would do as a <? extends Number>, but the compiler will tell you that <? extends Number> cannot be converted to Integer (that is, in human parlance, it is wrong that anything that extends number can be converted to Integer):
class Holder<T> {
T v;
T get() { return v; }
void set(T n) { v=n; }
}
class A {
public static void main(String[]args) {
Holder<? extends Number> he = new Holder();
Holder<? super Number> hs = new Holder();
Integer i;
Number n;
Object o;
// Producer Super: always gives an error except
// when consumer expects just Object
i = hs.get(); // <? super Number> cannot be converted to Integer
n = hs.get(); // <? super Number> cannot be converted to Number
// <? super Number> cannot be converted to ... (but
// there is no class between Number and Object)
o = hs.get();
// Consumer Super
hs.set(i);
hs.set(n);
hs.set(o); // Object cannot be converted to <? super Number>
// Producer Extends
i = he.get(); // <? extends Number> cannot be converted to Integer
n = he.get();
o = he.get();
// Consumer Extends: always gives an error
he.set(i); // Integer cannot be converted to <? extends Number>
he.set(n); // Number cannot be converted to <? extends Number>
he.set(o); // Object cannot be converted to <? extends Number>
}
}
hs.set(i); is ok because Integer can be converted to any superclass of Number (and not because Integer is a superclass of Number, which is not true).
EDIT added a comment about Consumer Extends and Producer Super -- they are not meaningful because they specify, correspondingly, nothing and just Object. You are advised to remember PECS because CEPS is never useful.
Using extends you can only get from the collection. You cannot put into it. Also, though super allows to both get and put, the return type during get is ? super T.
Example,
Order of inheritance is assumed as O > S > T > U > V
Using extends Keyword ,
Correct:
List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();
InCorrect:
List<? extends T> Object = new List<S>();
List<? extends T> Object = new List<O>();
super Keyword:
Correct:
List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();
InCorrect:
List<? super T> Object = new List<U>();
List<? super T> Object = new List<V>();
Adding object:
List Object = new List();
Object.add(new T()); //error
But Why error ?
Let's look at the Possibilities of initializations of List Object
List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();
If we use Object.add(new T()); then it will be correct only if
List<? extends T> Object = new List<T>();
But there are extra two possibilities
List Object = new List();
List Object = new List();
If we try to add (new T()) to the above two possibilities it will give an error because T is the superior class of U and V . we try to add a T object [which is (new T()) ] to List of type U and V . Higher class object(Base class) cannot be passed to lower class Object(Sub class).
Due to the extra two possibilities , Java gives you error even if you use the correct possilibity as Java don't know what Object you are referring to .So you can't add objects to List Object = new List(); as there are possibilities that are not valid.
Adding object:
List Object = new List();
Object.add(new T()); // compiles fine without error
Object.add(new U()); // compiles fine without error
Object.add(new V()); // compiles fine without error
Object.add(new S()); // error
Object.add(new O()); // error
But why error occurs in the above two ?
we can use Object.add(new T()); only on the below possibilities,
List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();
If we Tried to use Object.add(new T()) in
List Object = new List();
and
List Object = new List();
then it will give error
This is because
We can't add T object[which is new T()] to the List Object = new List(); because it is an object of type U . We can't add a T object[which is new T()] to U Object because T is a base class and U is a sub class . We can't add base class to subclass and that's why error occurs . This is same for the another case .
The generic wildcards target two primary needs:
Reading from a generic collection
Inserting into a generic collection
There are three ways to define a collection (variable) using generic wildcards. These are:
List<?> listUknown = new ArrayList<A>();
List<? extends A> listUknown = new ArrayList<A>();
List<? super A> listUknown = new ArrayList<A>();
List<?> means a list typed to an unknown type. This could be a List<A>, a List<B>, a List<String> etc.
List<? extends A> means a List of objects that are instances of the class A, or subclasses of A (e.g. B and C).
List<? super A> means that the list is typed to either the A class, or a superclass of A.
Read more : http://tutorials.jenkov.com/java-generics/wildcards.html
When to use extends and super
Wildcards are most useful in method parameters. They allow for the necessary flexibility in method interfaces.
People are often confused when to use extends and when to use super bounds. The rule of thumb is the get-put principle. If you get something from a parametrized container, use extends.
int totalFuel(List<? extends Vehicle> list) {
int total = 0;
for(Vehicle v : list) {
total += v.getFuel();
}
return total;}
The method totalFuel gets Vehicles from the list, asks them about how much fuel they have, and computes the total.
If you put objects into a parameterized container, use super.
int totalValue(Valuer<? super Vehicle> valuer) {
int total = 0;
for(Vehicle v : vehicles) {
total += valuer.evaluate(v);
}
return total;}
The method totalValue puts Vehicles into the Valuer.
It's useful to know that extends bound is much more common than super.
I know Generics are invariant: for any two distinct types Type1 and Type2,
List< Type1> is neither a subtype nor a supertype of List<
Type2>
so
List<someObject> nums = new ArrayList<someObject>(); // this is correct
List<Object> nums = new ArrayList<subObject>(); // this is not correct
but
List<Number> nums = new ArrayList<Number>();
List<? super Number> sink = nums; ????(line 2)
let say if wildcard is Object so line 2 wil be
List<Object> sink = List<Number> nums
It seems that the invariant rule not applied here
can anyone explain to me why line 2 is compiled without error ?
Thank so much
If I am not wrong, you wish to have an explanation on how is the below valid code:
List<Number> nums = new ArrayList<Number>();
List<? super Number> sink = nums;
Invariance is property of the class on how its type parameter affects its subtyping.
Generics are invariant, but wild cards exist to help us with with sub-typing. They are not very useful as they dont representing anytype but represent a good hack. Below is valid
List<Animal> <: List<?>
List<?> <: List
Better example:
List<? super Animal> d1= new ArrayList<Animal>();
d1.add(new Animal());
d1.add(new Dog());
The above works, because d1 is of type List<? super Animal>. You can imagine add function to behave like:
boolean add(? super Animal e);
So to add method you can pass any variable that is subtype of ? super Animal. And
Animal <: ? super Animal
Dog <: ? super Animal
So adding a Dog or Animal works. So d1 acts as a list that can take any parameter of type Animal or subtype if it.
Similarly, you can also have below. But technically you cant add anything to this list. If there existed sometype in java that is subtype of every type, then you could properly add element of that type to it. Nothing Else.
ArrayList<? extends Animal> d1 = new ArrayList<Animal>();
Look at this answer for more info.
The invariant rule does indeed apply. The statement:
List<? super Number> sink = nums;
just doesn't work they way you are thinking.
I expect that you think that you can assign an object of type ArrayList to a variable of type List<? super Number> because the former is a subclass of the latter. This would break the invariant rule, but this is not what is happening.
The List<? super Number> variable represents the set of all possible List where someClass is Number or is an ancestor of Number.
let say if wildcard is Object so line 2 wil be
`List<Object> sink = List<Number> nums `
In this context the ? doesn't get set arbitrarily, so this doesn't happen.
Lets have:
List<Integer> li = new ArrayList<>();
List<Object> lo = new ArrayList<>();
l = lo;//not allowed
lo.add("string");
Integer i = li.get(0);//that is string there
That is why you cannot make such assignment. I think compiler doesn't treat direct assignment of new ArrayList<Integer>() other then existing variable li.
Please give me a hint as to what is going on here:
List<? extends Number> a = new ArrayList<Number>();
List<? extends Number> b = new ArrayList<Number>();
a.addAll(b); // ouch! compiler yells at me, see the block below:
/*
incompatible types
found : java.util.List<capture#714 of ? extends java.lang.Number>
required: java.util.List<java.lang.Number>
*/
This simple code does not compile. I vaguely remember something related to type captures, like those should be mostly used in interface specs, not the actual code, but I never got dumbfounded like that.
This of course might be fixed brute-forcefully, like that:
List<? extends Number> a = new ArrayList<Number>();
List<? extends Number> b = new ArrayList<Number>();
#SuppressWarnings({"unchecked"})
List<Number> aPlain = (List<Number>) a;
#SuppressWarnings({"unchecked"})
List<Number> bPlain = (List<Number>) b;
aPlain.addAll(bPlain);
So, do I really have to either give up captures in the declaration (the capture came to me from an interface, so I'll have to change some API), or stick with type casts with suppression annotations (which generally suck and complicates code a bit)?
You have essentially two lists of possibly different types. Because ? extends Number means a class which extends Number. So for list a it can be classA and for list b it can be for example classB. They are not compatible, they can be totally different.
The problem is that if you use List<? extends Number> you could actually do:
List<? extends Number> a = new ArrayList<Integer>();
List<? extends Number> b = new ArrayList<Double>();
a.addAll(b); //ouch, would add Doubles to an Integer list
The compiler can't tell from List<? extends Number> what the actual type parameter is and thus won't let you do the add operation.
You also shouldn't cast the lists to List<Number> if you get them as a parameter, since you could actually have a list of Integer objects and add Double objects to it.
In that case you better create a new List<Number> and add the objects from both lists:
List<Number> c = new ArrayList<Number>(a.size() + b.size());
c.addAll(a);
c.addAll(b);
Edit: in case you create both lists locally, you would not neet the ? wildcard anyway (since you'd always have List<Number>).
PECS (producer-extends, consumer-super)
You cannot put anything into a type declared with an EXTENDS wildcard
except for the value null, which belongs to every reference type
You cannot get anything out from a type declared with an SUPER
wildcard except for a value of type Object, which is a super type
of every reference type
Don't use the ? wildcard. It means "Some specific type I don't know", and since you don't know the type, you can't add anything to the list. Change your code to use List<Number> and everything will work.
This is fast becoming the most frequently asked Java question, in a hundred variations...
The thing is:
List<? extends Number> a = new ArrayList<Number>();
List<? extends Number> b = new ArrayList<Number>();
could also be read as:
List<x extends Number> a = new ArrayList<Number>();
List<y extends Number> b = new ArrayList<Number>();
How should the compiler know that x and y are the same?
Having the following simple class structure:
class A {
}
class B extends A {
}
class C extends B {
}
I'm creating an ArrayList to keep objects of the earlier created classes:
List<? extends A> list1 = new ArrayList<A>();
List<? extends B> list2 = new ArrayList<B>();
List<? extends C> list3 = new ArrayList<C>();
List<? super A> list4 = new ArrayList<A>();
List<? super B> list5 = new ArrayList<B>();
List<? super C> list6 = new ArrayList<C>();
To each of those lists I'm trying to add 1 object of each earlier created class: A,B,C. The only possible combination is:
adding object of class A,B,C to list4
adding object of class B and C to list5
adding object of class C to list list6.
The rest of the tries gives compiler errors, such us:
The method add(capture#1-of ? extends
A) in the type List is not applicable for the
arguments (A)
Why can't I add any object of class A,B,C to list1/2/3?
Why e.g. list4 accepts objects of classes A,B,C if they are supposed to be a super class of class A, as the list4 is defined?
"? extends A" means "some type derived from A (or A itself)". So for instance, a List<ByteArrayOutputStream> is compatible with List<? extends OutputStream> - but you shouldn't be able to add a FileOutputStream to such a list - it's meant to be a List<ByteArrayOutputStream>! All you know is that anything you fetch from the list will be an OutputStream of some kind.
"? super A" means "some type which is a superclass of A (or A itself)". So for instance, a List<OutputStream> is compatible with List<? super ByteArrayOutputStream>. You can definitely add a ByteArrayOutputStream to such a list - but if you fetch an item from the list, you can't really guarantee much about it.
See Angelika Langer's Generics FAQ for much more information.
The type definition List<? extends A> is not usable for a mutable List - the explanation given in Java generics Java Generics Pdf is
The add() method takes arguments of type E, the element type of the
collection.
When the actual type parameter is ?, it stands for some unknown type.
Any parameter
we pass to add would have to be a subtype of this unknown type. Since we
don’t know
what type that is, we cannot pass anything in.
However, when the typedef is List<? super A> then the type parameter ? is implicitly typed.
List<? extends A> list1
It is a list, whose type element could be any unknown subclass of A. For example, it could be a D subclass. Therefore, you can't add anything to it, it could be wrong...
List<? super A> list4
It is a list, whose type element could be A, or a superclass of A (does not exist in that case, except Object). Therefore, you can add A objects to it, or any subclass of A such as B or C.
It doesn't work that way.
You should use <? extends T> when you create function which argument is collection of unknown subtype of some type, and you want to fetch objects from that collection:
int sum(Collection<? extends Integer> collection) {
for (Integer : collection) {
// do sth
}
// ...
}
You cannot add new items to this collection, because you don't know which concrete type is this collection holding. All you know is that that type extends Integer.
You use <? super T> when you want to add new items of type T to collection and return that collection, but then you cannot guarantee what you can retrieve from it and you have to cast result of get() or check its type. You can safely add items of type T and subtypes, and retrieve items of type T.
List<? super B> allows you to use Lists of any supertype of B, i.e. list5 = new ArrayList<A>();
or list5 = new ArrayList<Object>();
You can safely add B (and subtypes) to every list that use supertypes of B, but you can not add any supertype of B. Imagine this:
public void myAdd(List<? super B> lst) {
lst.add(new Object()) //this is a supertype of B (compile time error)
}
...
ArrayList<A> list = new ArrayList<A>();
myAdd(list); //tries to add Object to a list of type A