generic VS wildcards in Java - java

I have these two methods under a generic class:
public class Container<S> {
public void f2(List<Object> l1, List<?> l2) {
l1 = l2; //compilation error row #1
}
public void f3(List<?> c, List<S> l) {
c = l; //ok row #2
l = c; //compilation error row #3
}
}
I really don't undersatand why row 2 is ok- if I transfer this method two lists, one is an object type list and one is a Strings one, I shoule get a compilation error?
would really appriciate to understadn why each row should/shouldn't be compiled.

Why does this compile?
List<?> c, List<S> l;
c = l; // OK
List<?> means (more or less) “list of something” (or more formally “list of unknown”), and a List<S>is one of those.
——
Why does this not compile?
List<Object> l1, List<?> l2;
l1 = l2; //compilation error
If it were allowed, you could then add anything (eg String) to l1, but if l2 is anything other than List<Object> (eg Integer), you’d be putting the wrong type in l2. That’s why this assignment is not allowed.
The other compilation error is more subtle, and also doesn’t have a use case - that is, there’s no reason to assign a typed list to an untyped one - but the wildcard ? really means “unknown, but specific”. This is not the same as “anything”. It’s “something”, but we don’t know what. Type S is something, but the compiler can’t verify that it’s the same something as S.

Generics and wildcards are powerful because they ensure better typechecking during compile time, that's the main purpose of using them. To make sure your code won't break at runtime due to poor type checking.
Source
Although Integer is a subtype of Number, List<Integer> is not a subtype of List<Number> and, in fact, these two types are not related. The common parent of List<Number> and List<Integer> is List<?>.
In order to create a relationship between these classes so that the code can access Number's methods through List<Integer>'s elements, use an upper bounded wildcard:
List<? extends Integer> intList = new ArrayList<>();
List<? extends Number> numList = intList; // OK. List<? extends Integer> is a subtype of List<? extends Number>
Because Integer is a subtype of Number, and numList is a list of Number objects, a relationship now exists between intList (a list of Integer objects) and numList. The following diagram shows the relationships between several List classes declared with both upper and lower bounded wildcards.
To make use of subtyping and polymophism with wildcards you have to use bounded wildcards.
public void f2(List<Object> l1, List<?> l2) {
l1 = l2; //compilation error row #1
}
//correct way of doing it
public void f2(List<? extends Object> l3, List<?> l4) {
l3 = l4;
}
Compiler doesn't see l2 as a subtype of l1
public void f3(List<?> c, List<S> l) {
c = l; //ok row #2
l = c; //compilation error row #3
}
Compiler doesn't see c as a subtype of l (rightly so, this could lead to runtime errors).

Related

Java wildcard parameterized type incompatible [duplicate]

Let's first consider a simple scenario (see complete source on ideone.com):
import java.util.*;
public class TwoListsOfUnknowns {
static void doNothing(List<?> list1, List<?> list2) { }
public static void main(String[] args) {
List<String> list1 = null;
List<Integer> list2 = null;
doNothing(list1, list2); // compiles fine!
}
}
The two wildcards are unrelated, which is why you can call doNothing with a List<String> and a List<Integer>. In other words, the two ? can refer to entirely different types. Hence the following does not compile, which is to be expected (also on ideone.com):
import java.util.*;
public class TwoListsOfUnknowns2 {
static void doSomethingIllegal(List<?> list1, List<?> list2) {
list1.addAll(list2); // DOES NOT COMPILE!!!
// The method addAll(Collection<? extends capture#1-of ?>)
// in the type List<capture#1-of ?> is not applicable for
// the arguments (List<capture#2-of ?>)
}
}
So far so good, but here's where things start to get very confusing (as seen on ideone.com):
import java.util.*;
public class LOLUnknowns1 {
static void probablyIllegal(List<List<?>> lol, List<?> list) {
lol.add(list); // this compiles!! how come???
}
}
The above code compiles for me in Eclipse and on sun-jdk-1.6.0.17 in ideone.com, but should it? Is it not possible that we have a List<List<Integer>> lol and a List<String> list, the analogous two unrelated wildcards situations from TwoListsOfUnknowns?
In fact the following slight modification towards that direction does not compile, which is to be expected (as seen on ideone.com):
import java.util.*;
public class LOLUnknowns2 {
static void rightfullyIllegal(
List<List<? extends Number>> lol, List<?> list) {
lol.add(list); // DOES NOT COMPILE! As expected!!!
// The method add(List<? extends Number>) in the type
// List<List<? extends Number>> is not applicable for
// the arguments (List<capture#1-of ?>)
}
}
So it looks like the compiler is doing its job, but then we get this (as seen on ideone.com):
import java.util.*;
public class LOLUnknowns3 {
static void probablyIllegalAgain(
List<List<? extends Number>> lol, List<? extends Number> list) {
lol.add(list); // compiles fine!!! how come???
}
}
Again, we may have e.g. a List<List<Integer>> lol and a List<Float> list, so this shouldn't compile, right?
In fact, let's go back to the simpler LOLUnknowns1 (two unbounded wildcards) and try to see if we can in fact invoke probablyIllegal in any way. Let's try the "easy" case first and choose the same type for the two wildcards (as seen on ideone.com):
import java.util.*;
public class LOLUnknowns1a {
static void probablyIllegal(List<List<?>> lol, List<?> list) {
lol.add(list); // this compiles!! how come???
}
public static void main(String[] args) {
List<List<String>> lol = null;
List<String> list = null;
probablyIllegal(lol, list); // DOES NOT COMPILE!!
// The method probablyIllegal(List<List<?>>, List<?>)
// in the type LOLUnknowns1a is not applicable for the
// arguments (List<List<String>>, List<String>)
}
}
This makes no sense! Here we aren't even trying to use two different types, and it doesn't compile! Making it a List<List<Integer>> lol and List<String> list also gives a similar compilation error! In fact, from my experimentation, the only way that the code compiles is if the first argument is an explicit null type (as seen on ideone.com):
import java.util.*;
public class LOLUnknowns1b {
static void probablyIllegal(List<List<?>> lol, List<?> list) {
lol.add(list); // this compiles!! how come???
}
public static void main(String[] args) {
List<String> list = null;
probablyIllegal(null, list); // compiles fine!
// throws NullPointerException at run-time
}
}
So the questions are, with regards to LOLUnknowns1, LOLUnknowns1a and LOLUnknowns1b:
What types of arguments does probablyIllegal accept?
Should lol.add(list); compile at all? Is it typesafe?
Is this a compiler bug or am I misunderstanding the capture conversion rules for wildcards?
Appendix A: Double LOL?
In case anyone is curious, this compiles fine (as seen on ideone.com):
import java.util.*;
public class DoubleLOL {
static void omg2xLOL(List<List<?>> lol1, List<List<?>> lol2) {
// compiles just fine!!!
lol1.addAll(lol2);
lol2.addAll(lol1);
}
}
Appendix B: Nested wildcards -- what do they really mean???
Further investigation indicates that perhaps multiple wildcards has nothing to do with the problem, but rather a nested wildcard is the source of the confusion.
import java.util.*;
public class IntoTheWild {
public static void main(String[] args) {
List<?> list = new ArrayList<String>(); // compiles fine!
List<List<?>> lol = new ArrayList<List<String>>(); // DOES NOT COMPILE!!!
// Type mismatch: cannot convert from
// ArrayList<List<String>> to List<List<?>>
}
}
So it looks perhaps a List<List<String>> is not a List<List<?>>. In fact, while any List<E> is a List<?>, it doesn't look like any List<List<E>> is a List<List<?>> (as seen on ideone.com):
import java.util.*;
public class IntoTheWild2 {
static <E> List<?> makeItWild(List<E> list) {
return list; // compiles fine!
}
static <E> List<List<?>> makeItWildLOL(List<List<E>> lol) {
return lol; // DOES NOT COMPILE!!!
// Type mismatch: cannot convert from
// List<List<E>> to List<List<?>>
}
}
A new question arises, then: just what is a List<List<?>>?
As Appendix B indicates, this has nothing to do with multiple wildcards, but rather, misunderstanding what List<List<?>> really means.
Let's first remind ourselves what it means that Java generics is invariant:
An Integer is a Number
A List<Integer> is NOT a List<Number>
A List<Integer> IS a List<? extends Number>
We now simply apply the same argument to our nested list situation (see appendix for more details):
A List<String> is (captureable by) a List<?>
A List<List<String>> is NOT (captureable by) a List<List<?>>
A List<List<String>> IS (captureable by) a List<? extends List<?>>
With this understanding, all of the snippets in the question can be explained. The confusion arises in (falsely) believing that a type like List<List<?>> can capture types like List<List<String>>, List<List<Integer>>, etc. This is NOT true.
That is, a List<List<?>>:
is NOT a list whose elements are lists of some one unknown type.
... that would be a List<? extends List<?>>
Instead, it's a list whose elements are lists of ANY type.
Snippets
Here's a snippet to illustrate the above points:
List<List<?>> lolAny = new ArrayList<List<?>>();
lolAny.add(new ArrayList<Integer>());
lolAny.add(new ArrayList<String>());
// lolAny = new ArrayList<List<String>>(); // DOES NOT COMPILE!!
List<? extends List<?>> lolSome;
lolSome = new ArrayList<List<String>>();
lolSome = new ArrayList<List<Integer>>();
More snippets
Here's yet another example with bounded nested wildcard:
List<List<? extends Number>> lolAnyNum = new ArrayList<List<? extends Number>>();
lolAnyNum.add(new ArrayList<Integer>());
lolAnyNum.add(new ArrayList<Float>());
// lolAnyNum.add(new ArrayList<String>()); // DOES NOT COMPILE!!
// lolAnyNum = new ArrayList<List<Integer>>(); // DOES NOT COMPILE!!
List<? extends List<? extends Number>> lolSomeNum;
lolSomeNum = new ArrayList<List<Integer>>();
lolSomeNum = new ArrayList<List<Float>>();
// lolSomeNum = new ArrayList<List<String>>(); // DOES NOT COMPILE!!
Back to the question
To go back to the snippets in the question, the following behaves as expected (as seen on ideone.com):
public class LOLUnknowns1d {
static void nowDefinitelyIllegal(List<? extends List<?>> lol, List<?> list) {
lol.add(list); // DOES NOT COMPILE!!!
// The method add(capture#1-of ? extends List<?>) in the
// type List<capture#1-of ? extends List<?>> is not
// applicable for the arguments (List<capture#3-of ?>)
}
public static void main(String[] args) {
List<Object> list = null;
List<List<String>> lolString = null;
List<List<Integer>> lolInteger = null;
// these casts are valid
nowDefinitelyIllegal(lolString, list);
nowDefinitelyIllegal(lolInteger, list);
}
}
lol.add(list); is illegal because we may have a List<List<String>> lol and a List<Object> list. In fact, if we comment out the offending statement, the code compiles and that's exactly what we have with the first invocation in main.
All of the probablyIllegal methods in the question, aren't illegal. They are all perfectly legal and typesafe. There is absolutely no bug in the compiler. It is doing exactly what it's supposed to do.
References
Angelika Langer's Java Generics FAQ
Which super-subtype relationships exist among instantiations of generic types?
Can I create an object whose type is a wildcard parameterized type?
JLS 5.1.10 Capture Conversion
Related questions
Any simple way to explain why I cannot do List<Animal> animals = new ArrayList<Dog>()?
Java nested wildcard generic won’t compile
Appendix: The rules of capture conversion
(This was brought up in the first revision of the answer; it's a worthy supplement to the type invariant argument.)
5.1.10 Capture Conversion
Let G name a generic type declaration with n formal type parameters A1…An with corresponding bounds U1…Un. There exists a capture conversion from G<T1…Tn> to G<S1…Sn>, where, for 1 <= i <= n:
If Ti is a wildcard type argument of the form ? then …
If Ti is a wildcard type argument of the form ? extends Bi, then …
If Ti is a wildcard type argument of the form ? super Bi, then …
Otherwise, Si = Ti.
Capture conversion is not applied recursively.
This section can be confusing, especially with regards to the non-recursive application of the capture conversion (hereby CC), but the key is that not all ? can CC; it depends on where it appears. There is no recursive application in rule 4, but when rules 2 or 3 applies, then the respective Bi may itself be the result of a CC.
Let's work through a few simple examples:
List<?> can CC List<String>
The ? can CC by rule 1
List<? extends Number> can CC List<Integer>
The ? can CC by rule 2
In applying rule 2, Bi is simply Number
List<? extends Number> can NOT CC List<String>
The ? can CC by rule 2, but compile time error occurs due to incompatible types
Now let's try some nesting:
List<List<?>> can NOT CC List<List<String>>
Rule 4 applies, and CC is not recursive, so the ? can NOT CC
List<? extends List<?>> can CC List<List<String>>
The first ? can CC by rule 2
In applying rule 2, Bi is now a List<?>, which can CC List<String>
Both ? can CC
List<? extends List<? extends Number>> can CC List<List<Integer>>
The first ? can CC by rule 2
In applying rule 2, Bi is now a List<? extends Number>, which can CC List<Integer>
Both ? can CC
List<? extends List<? extends Number>> can NOT CC List<List<Integer>>
The first ? can CC by rule 2
In applying rule 2, Bi is now a List<? extends Number>, which can CC, but gives a compile time error when applied to List<Integer>
Both ? can CC
To further illustrate why some ? can CC and others can't, consider the following rule: you can NOT directly instantiate a wildcard type. That is, the following gives a compile time error:
// WildSnippet1
new HashMap<?,?>(); // DOES NOT COMPILE!!!
new HashMap<List<?>, ?>(); // DOES NOT COMPILE!!!
new HashMap<?, Set<?>>(); // DOES NOT COMPILE!!!
However, the following compiles just fine:
// WildSnippet2
new HashMap<List<?>,Set<?>>(); // compiles fine!
new HashMap<Map<?,?>, Map<?,Map<?,?>>>(); // compiles fine!
The reason WildSnippet2 compiles is because, as explained above, none of the ? can CC. In WildSnippet1, either the K or the V (or both) of the HashMap<K,V> can CC, which makes the direct instantiation through new illegal.
No argument with generics should be accepted. In the case of LOLUnknowns1b the null is accepted as if the first argument was typed as List. For example this does compile :
List lol = null;
List<String> list = null;
probablyIllegal(lol, list);
IMHO lol.add(list); shouldn't even compile but as lol.add() needs an argument of type List<?> and as list fits in List<?> it works.
A strange example which make me think of this theory is :
static void probablyIllegalAgain(List<List<? extends Number>> lol, List<? extends Integer> list) {
lol.add(list); // compiles fine!!! how come???
}
lol.add() needs an argument of type List<? extends Number> and list is typed as List<? extends Integer>, it fits in. It won't work if it doesn't match.
Same thing for the double LOL, and other nested wildcards, as long as the first capture matches the second one, everything is okay (and souldn't be).
Again, I'm not sure but it does really seem like a bug.
I'm glad to not be the only one to use lol variables all the time.
Resources :
http://www.angelikalanger.com, a FAQ about generics
EDITs :
Added comment about the Double Lol
And nested wildcards.
not an expert, but I think I can understand it.
let's change your example to something equivalent, but with more distinguishing types:
static void probablyIllegal(List<Class<?>> x, Class<?> y) {
x.add(y); // this compiles!! how come???
}
let's change List to [] to be more illuminating:
static void probablyIllegal(Class<?>[] x, Class<?> y) {
x.add(y); // this compiles!! how come???
}
now, x is not an array of some type of class. it is an array of any type of class. it can contain a Class<String> and a Class<Int>. this cannot be expressed with ordinary type parameter:
static<T> void probablyIllegal(Class<T>[] x //homogeneous! not the same!
Class<?> is a super type of Class<T> for any T. If we think a type is a set of objects, set Class<?> is the union of all sets of Class<T> for all T. (does it include itselft? I dont know...)

Why can't add element in a upper bound generics List?

I have a list with upper bound generics.
List<? extends Number> l = new ArrayList<>();
l.add(new Integer(3)); //ERROR
l.add(new Double(3.3)); // ERROR
I don't understand the problem, because Integer and Double extend Number.
List<? extends Number> does not mean "a list that can hold all objects of subclasses of Number", it means "a list parameterized to one concrete class that extends Number". It's not the contents of the list itself you are defining, it's what the parameterized type of the actual list-object assigned to the variable can be (boy, this is harder to explain than it is to understand :) )
So, you can do:
List<? extends Number> l = new ArrayList<Integer>();
List<? extends Number> l = new ArrayList<Double>();
If you want a list that is able to hold any object of class Number or its subclasses, just do this:
List<Number> l = new ArrayList<>();
l.add(new Integer(33));
l.add(new Double(33.3d));
(The boxing of the values inserted is unnecessary, but there for clarity..)
Upper bounded and unbounded wildcard collections are immutable.
For example, you cannot do:
List<? extends Number> myList = new ArrayList<Integer>();
myList.add(new Integer(3)); //will not compile
This fails to compile because java does not know what type of List List<? extends Number> is at compilation time.
So the example above, at compile time, myList could be List<Double>or List<Integer> or a List of any subclass of Number. And since you cannot add a Double to a List<Integer> or vise-versa, the compilation fails.
The problem with upper-bounded generics is that the compiler doesn't know the exact type that is going to be used, for example:
List<? extends Number> upperBounded = new ArrayList<Integer>();
upperBounded = new ArrayList<Double>();
upperBounded can be a List of Integers as well as a list of Doubles or any other descendant of Number. Now imagine what will happen if Java allowed us to add any subclass of Number to that List.
List<? extends Number> upperBounded = new ArrayList<Integer>();
upperBounded.add(1.0); // compile-time error
Good thing that Java prevents us from doing that because this would introduce a lot of bugs.
The same applies to the unbounded generic types.
Now what about the lower-bounded generics?
List<? super Integer> lowerBounded = new ArrayList<Integer>();
lowerBounded.add(0);
This is is perfectly fine because we are safe to assume that we can add Integers to any List of an Integer superclass and this will not lead to inconsistency, for example:
List<? super Integer> lowerBounded = new ArrayList<Number>();
lowerBounded.add(0);
Here we have a reference that allows a List of Integers or one of the Integer supertypes. ArrayList of Numbers fits into that definition, so we are able to assign it to that reference. Then we can add an Integer to this list, and this is also fine because a List of Numbers can contain an Integer (because Integer is a Number).
It may be surprising, but you can't add anything not assignable to Integer to this list for the same reason as I explained above for lower-bounded generics.
List<? super Integer> lowerBounded = new ArrayList<Number>();
lowerBounded.add(1.0); // compile-time error
As the compiler does not know the List of which exact type will be used, it does not allow us to add anything that could potentially break the promise given by generics.
Hope that helps.
Because List<? extends Number> means that your variable l holds a value of type List with concrete (but unknown!) type argument that extends Number.
You can add only null, because l can hold a List<MyClass> for example, where MyClass is your class that extends Number, but nor Integer, nor Double value can be casted to MyClass.
I will add one more way to add the subtypes of Number to this list. i.e
List<? super Number> l = new ArrayList<>();
l.add(new Integer(3)); //OK
l.add(new Double(3.3)); //OK
This is allowed since the list is parameterized to be any unknown supertype of Number class. so, compiler allows the known subtype of Number. i.e Integer and Double types
Yes in case of
List<? extends Number>
this is just a reference, may be the actual object will be
List<Integer>
so you should not be allowed to add new Double(5.0) in a list of Integer.

Java Generics -- Assigning a list of subclass to a list of superclass

I have a basic question regarding assignment of a list of subclass to a list of superclass.
So I have something like the following:
Class B extends A;
List <B> bList = new ArrayList<B>();
List <A> aList = bList;
Why does this last assignment fail? Sorry for the newbie question
To explain this, let me substitute "B" with Integer and "A" with Number. This is just to make it a little easier to explain.
Class Integer extends Number;
List <Integer> iList = new ArrayList<Integer>();
List <Number> nList = iList // will fail
The reason this would fail is because nList can take any Number -- it can take Integer, it can take Double, or for that matter any subclass of Number. However, this is not true for iList. You cannot add a Double to iList because it accepts only Integer and its subclasses. Hope this helps explain it to you.
When you declare a List of items of type A, only items of type A can be added or removed from the List. If you need to include subclasses of A, use the generic wildcard ? extends A to indicate so. Your code should therefore be:
List <? extends A> aList = bList;
List<B> is not List<A>:
Through example: let say you have class B1 extends A{} and class B2 extends A{}
then (if you would be able to do that:
List<B1> b1 = new AList<B1>();
List<A> a = b1;
List<B2> b2 = new AList<B2>();
by the hypothesis, you should be able to do
a.add(new B2())
but this is wrong.
If you try the same thing but using arrays instead of lists, it will compile and throw exception in runtime.
We say that arrays are covariant and generics are invariant.
to make the code compile you have the wite it:
List<? extends A> a = b;
this says that a is a list of some subtype of A. _But you don know which one. Because of that you can't do a.put(X)
List<B> and List<A> are invariant type. What you need is covariant type. In this case, it is List<? extends A>.
Because generics are strict type safe.
You can have
List<? extends A> aList = bList;
It says aList can hold list of any type which is an A
Because List<B> does not extend List<A>. For example, Integer extends Number and so does Long. So List<Number> can contain both Integer and Long. So if you assign List<Integer> to List<Number> you will be able to add Long to your list of integers.
You can declare
List<? super B> superB;
And that would allow assignment to superB of any list that contains B and its super classes.
But it's not the same as in your case aList=bList.
or
List<? extends A> extendsA;
Examples
List<? super Integer> superA;
superA = new ArrayList<Number>();
List<? extends Number> extendsNumber;
extendsNumber = new ArrayList<Integer>();
While at first glance you might think that
Class B extends A;
List <B> bList = new ArrayList<B>();
List <A> aList = bList;
should work, the problem is obvious when you imagine actually using these lists:
A something = new A();
aList.add( something ); // Should work because aList is a list of A's
but aList was assigned to bList, so that should be the same as
bList.add( something ); // Here's the problem
bList.add() takes a B, but something is an A, and an A is not a B!
And that's why generics should be (and are) strict type safe.

How can I add to List<? extends Number> data structures?

I have a List which is declared like this :
List<? extends Number> foo3 = new ArrayList<Integer>();
I tried to add 3 to foo3. However I get an error message like this:
The method add(capture#1-of ? extends Number) in the type List<capture#1-of ?
extends Number> is not applicable for the arguments (ExtendsNumber)
Sorry, but you can't.
The wildcard declaration of List<? extends Number> foo3 means that the variable foo3 can hold any value from a family of types (rather than any value of a specific type). It means that any of these are legal assignments:
List<? extends Number> foo3 = new ArrayList<Number>(); // Number "extends" Number
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>(); // Double extends Number
So, given this, what type of object could you add to List foo3 that would be legal after any of 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.
The reverse logic applies to super, e.g. List<? super T>. These are legal:
List<? super Number> foo3 = new ArrayList<Number>(); // Number is a "super" of Number
List<? super Number> foo3 = new ArrayList<Object>(); // Object is a "super" of Number
You can't read the specific type T (e.g. Number) from List<? super T> because you can't guarantee what kind of List it is really pointing to. The only "guarantee" you have is you are able to add a value of type T (or any subclass of T) without violating the integrity of the list being pointed to.
The perfect example of this is the signature for Collections.copy():
public static <T> void copy(List<? super T> dest, List<? extends T> src)
Notice how the src list declaration uses extends to allow me to pass any List from a family of related List types and still guarantee it will produce values of type T or subclasses of T. But you cannot add to the src list.
The dest list declaration uses super to allow me to pass any List from a family of related List types and still guarantee I can write a value of a specific type T to that list. But it cannot be guaranteed to read the values of specific type T if I read from the list.
So now, thanks to generics wildcards, I can do any of these calls with that single method:
// copy(dest, src)
Collections.copy(new ArrayList<Number>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Number>(), new ArrayList<Integer>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Double>());
Consider this confusing and very wide code to exercise your brain. The commented out lines are illegal and the reason why is stated to the extreme right of the line (need to scroll to see some of them):
List<Number> listNumber_ListNumber = new ArrayList<Number>();
//List<Number> listNumber_ListInteger = new ArrayList<Integer>(); // error - can assign only exactly <Number>
//List<Number> listNumber_ListDouble = new ArrayList<Double>(); // error - can assign only exactly <Number>
List<? extends Number> listExtendsNumber_ListNumber = new ArrayList<Number>();
List<? extends Number> listExtendsNumber_ListInteger = new ArrayList<Integer>();
List<? extends Number> listExtendsNumber_ListDouble = new ArrayList<Double>();
List<? super Number> listSuperNumber_ListNumber = new ArrayList<Number>();
//List<? super Number> listSuperNumber_ListInteger = new ArrayList<Integer>(); // error - Integer is not superclass of Number
//List<? super Number> listSuperNumber_ListDouble = new ArrayList<Double>(); // error - Double is not superclass of Number
//List<Integer> listInteger_ListNumber = new ArrayList<Number>(); // error - can assign only exactly <Integer>
List<Integer> listInteger_ListInteger = new ArrayList<Integer>();
//List<Integer> listInteger_ListDouble = new ArrayList<Double>(); // error - can assign only exactly <Integer>
//List<? extends Integer> listExtendsInteger_ListNumber = new ArrayList<Number>(); // error - Number is not a subclass of Integer
List<? extends Integer> listExtendsInteger_ListInteger = new ArrayList<Integer>();
//List<? extends Integer> listExtendsInteger_ListDouble = new ArrayList<Double>(); // error - Double is not a subclass of Integer
List<? super Integer> listSuperInteger_ListNumber = new ArrayList<Number>();
List<? super Integer> listSuperInteger_ListInteger = new ArrayList<Integer>();
//List<? super Integer> listSuperInteger_ListDouble = new ArrayList<Double>(); // error - Double is not a superclass of Integer
listNumber_ListNumber.add(3); // ok - allowed to add Integer to exactly List<Number>
// These next 3 are compile errors for the same reason:
// You don't know what kind of List<T> is really
// being referenced - it may not be able to hold an Integer.
// You can't add anything (not Object, Number, Integer,
// nor Double) to List<? extends Number>
//listExtendsNumber_ListNumber.add(3); // error - can't add Integer to *possible* List<Double>, even though it is really List<Number>
//listExtendsNumber_ListInteger.add(3); // error - can't add Integer to *possible* List<Double>, even though it is really List<Integer>
//listExtendsNumber_ListDouble.add(3); // error - can't add Integer to *possible* List<Double>, especially since it is really List<Double>
listSuperNumber_ListNumber.add(3); // ok - allowed to add Integer to List<Number> or List<Object>
listInteger_ListInteger.add(3); // ok - allowed to add Integer to exactly List<Integer> (duh)
// This fails for same reason above - you can't
// guarantee what kind of List the var is really
// pointing to
//listExtendsInteger_ListInteger.add(3); // error - can't add Integer to *possible* List<X> that is only allowed to hold X's
listSuperInteger_ListNumber.add(3); // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>
listSuperInteger_ListInteger.add(3); // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>
You can't (without unsafe casts). You can only read from them.
The problem is that you don't know what exactly the list is a list of. It could be a list of any subclass of Number, so when you try to put an element into it, you don't know that the element actually fits into the list.
For example the List might be a list of Bytes, so it would be an error to put a Float into it.
It has been confusing to me even though I read answers here, until I found the comment by Pavel Minaev:
Note that List < ? extends Number > does not mean "list of objects of
different types, all of which extend Number". It means "list of
objects of a single type which extends Number"
After this I was able to understand BertF awesome explanation. List < ? extends Number > means ? could be of any type extending Number(Integer, Double, etc) and its not clearified in declaration ( List < ? extends Number > list ) that which of them it is, so when u wanna use add method its not known if the input is of the same type or not; what is the type at all?
So the elements of List < ? extends Number > could only be set when constructing.
Also note this: When we're using templates we are telling the compiler what type we're messing with. T for example holds that type for us, but not ? does the same
I gotta say.. This is one of the dirty ones to explain/learn
"List '<' ? extends Number> is actually an upper bound wildcard !
The upper-bounded wildcard says that any class that extends Number or Number itself can be used as the formal parameter type:
The problem stems from the fact that Java doesn’t know what type List really is.
It has to be an EXACT and UNIQUE Type. I hope it helps :)
You could do this instead:
List<Number> foo3 = new ArrayList<Number>();
foo3.add(3);
You can fudge it by creating a reference to the List with a different type.
(These are the "unsafe casts" mentioned by sepp2k.)
List<? extends Number> list = new ArrayList<Integer>();
// This will not compile
//list.add(100);
// WORKS, BUT NOT IDEAL
List untypedList = (List)list;
// It will let you add a number
untypedList.add(200);
// But it will also let you add a String! BAD!
untypedList.add("foo");
// YOU PROBABLY WANT THIS
// This is safer, because it will (partially) check the type of anything you add
List<Number> superclassedList = (List<Number>)(List<?>)list;
// It will let you add an integer
superclassedList.add(200);
// It won't let you add a String
//superclassedList.add("foo");
// But it will let you add a Float, which isn't really correct
superclassedList.add(3.141);
// ********************
// So you are responsible for ensuring you only add/set Integers when you have
// been given an ArrayList<Integer>
// ********************
// EVEN BETTER
// If you can, if you know the type, then use List<Integer> instead of List<Number>
List<Integer> trulyclassedList = (List<Integer>)(List<?>)list;
// That will prevent you from adding a Float
//trulyclassedList.add(3.141);
System.out.println("list: " + list);
Because untypedList, superclassedList and trulyclassedList are just references to list, you will still be adding elements to the original ArrayList.
You don't actually need to use (List<?>) in the example above, but you might need it in your code, depending on the type of list you were given.
Note that using ? will give you compiler warnings, until you put this above your function:
#SuppressWarnings("unchecked")
Because 3 is primitive (int), which does not extend Number, you can add its boxed type (Integer.valueOf(3)). Although it is displayed as an error in IDEA, it can still execute normally.

Java Generic Method Question

Consider this code:
public <T> List<T> meth(List<?> type)
{
System.out.println(type); // 1
return new ArrayList<String>(); // 2
}
It does not compile at line 2, saying that List is required.
Now, if it's changed to:
public <T> List<?> meth(List<T> type)
{
System.out.println(type); // 1
return new ArrayList<String>(); // 2
}
It does compile. Why? I thought the difference between declaring a generic type with T and using the wildcard was that, when using the wildcard, one cannot add new elements to a collection. Why would <?> allow a subtype of List to be returned? I'm missing something here, what's the explicit rule and how it's being applied?
The difference is in the return type declaration. List<String> is not a subtype of List<T>, but it is a subtype of List<?>.
List<?> makes no assumptions regarding its type variable, so the following statements are valid:
List<?> l0 = new ArrayList<String>();
List<?> l1 = new ArrayList<Object>();
List<? extends Number> ltemp = null;
List<?> l2 = ltemp;
List<T> assumes that the type argument will be resolved in the context of client (e.g. type use), when you declared it as List<String> or List<Object>. Within the method body, you cannot make any assumptions about it either.
In the first case, T is not necessarily a superclass of String. If you choose a T like Integer and call the method, it'll fail; so it won't compile. However, the second will compile as surely, any ArrayList<String> is a valid List of something.
As said earlier, String isn't a subtype of T, so it's why it does not work. However, this code works :
public <T> List<T> meth(List<?> type)
{
System.out.println(type); // 1
return new ArrayList<T>(); // 2
}
and is more in the idea of what you want, I think.

Categories