public class Demo {
public static void main(String[] args) {
ArrayList<Integer> list = getList();
System.out.println(list.toString());
list.add(1);
System.out.println(list.toString());
}
public static <T> ArrayList<T> getList(){
ArrayList<String> strings = new ArrayList<>();
strings.add("qwert");
return (ArrayList<T>) strings;
}
}
As the code shown above why it is allowed to assign to ArrayList
JDK11 out put
[qwert]
1
Why ArrayList<T> can assign to ArrayList<Integer>
It's because you have provided explicit typecast.
Probably, what you are not able to understand is how ArrayList<Integer> list can hold qwert. Again, it's because you have asked the compiler to trust you that you are putting Integer into it.
Eventually, all the type information is removed when it comes to runtime. You need to understand the concept of Type Erasure.
return (ArrayList<T>) strings;
Here, the String ArrayList is not directly casted to Integer ArrayList, instead, it is casted to T ArrayList. According to 'Target Type Inference' in the docs:
static <T> List<T> emptyList();
List<String> listOne = Collections.emptyList();
This statement is expecting an instance of List; this data type is the target type. Because the method emptyList returns a value of type List, the compiler infers that the type argument T must be the value String.
regarding the following code:
public class Test <T extends Comparable>{
public static void main(String[] args){
List<String> lst = Array.asList("abc","def");
System.out.println(func(lst));
}
public static boolean func(List<**here**> lst){
return lst.get(0).compareTo(lst.get(1)) == 0;
}
}
why writing " ? extends Comparable" here would compile , and writing "Comparable" would not compile?
thanks in advance.
This happens because generics are invariant. Even if String is a Comparable, meaning:
String s = "";
Comparable c = s; // would work
Generics of these would not work:
List<Comparable> listC = List.of();
List<String> listS = List.of();
listC = listS; // will fail
And this would not work no matter what is the relationship between Comparable and String.
When you change the definition of that method to:
public static boolean func(List<? extends Comparable> lst) {
...
}
This is said that: wildcard with an extends-bound makes the type covariant.
This means that :
List<? extends Comparable> listC = List.of();
List<String> listS = List.of();
listC = listS; // would work here
Or in simpler words it means that List<String> is a subtype of List<? extends Comparable>.
There is a small price to pay now, because listC is now a producer of elements, meaning you can take elements out of it, but you can not put anything into it.
And well after you understand this, you are not done yet, because the definition of that method would be entirely correct, when written like this:
public static <T extends Comparable<? super T>> boolean func(List<T> lst) {
.....
}
This is because List<SubClass> cannot be cast to List<BaseClass>. Let us suppose that it could
List<String> sList = new ArrayList<>();
List<Comparable> cList = (List<Comparable>) sList;
cList.add(5);
This would be a problem because the integer 5 is not a String and should not be placed in a List<String>.
By using ? extends Comparable you are saying that the function can take a list of anything that has Comparable as a base class (e.g. List<Comparable>, List<String>, List<Integer>, etc.)
To more correctly define your function you should do the following:
public static <T extends Comparable<T>> boolean func(List<T> lst) {}
This enforces that the type is comparable with itself. Your function compares the first and second element in the list, so it is best to ensure that each element in the list is actually comparable with every other element.
Because
List<String> lst = Array.asList("abc","def");
lst list has generic type String, not Comparable.
String class, hovewer, implement Comparable<String> interface, so it fits in ? extends Comparable generic type.
Arraylist with List<interfaceI> and List<? extends InterfaceI> both will have objects of classes implementing interfaceI. Then when what should be used?
Suppose Foo and Bar are two classes implementing InterfaceI.
The second one (List<? extends InterfaceI>) doesn't allow adding anything to the list (except null), since the type that the list contains is unknown: it could be a List<Foo> or a List<Bar>: you just don't know.
So you usually use this notation for a method argument, when you want the method to read the elements of the list passed as argument, and want the caller to be able to call your method with a List<InterfaceI>, a List<Foo> or a List<Bar>. Using List<InterfaceI> as argument would only accept lists of type List<InterfaceI>.
Let's take a concrete example: you want to compute the maximum double value of a list of numbers. Such a method doesn't need to add or set anything to the list. All it does is iterating on the elements, get each number and compute their maximum. The signature could be
public double max(List<Number> list);
But then, you won't be able to do
List<Integer> ints = new ArrayList<>();
max(ints);
The only way to call this method is to do
List<Number> ints = new ArrayList<>();
max(ints);
Whereas if you declare the method as
public double max(List<? extends Number> list);
then you can do
List<Integer> ints = new ArrayList<>();
List<Long> longs = new ArrayList<>();
max(ints);
max(longs)
The difference is that if you declare your list as List<? extends S> myList, the myList variable can by list of any type that extends S so associations like shown below will work:
public class Clazz implements S{}
List<? extends S> myList = new List<Clazz>(); // its fine as Clazz extends S
List<? extends S> myList = new List<S>(); // its fine as well
List<? extends S> myList = new List<Object>(); // ooooops it wil not work
But in such case you will not be abe to PUT anything to such list as you cannot guarantee the exact type of object that is held by list implementation assigned to myList
If you declare List<S> myList than you will be able to PUT and GET objects from list, as you are sure what is in it, however assignments from above will not work!
public class Clazz implements S{}
List<S> myList = new List<Clazz>(); // no way!
List<S> myList = new List<S>(); //thats perfectly fine! - You can PUT new S in it;
List<S> myList = new List<Object>(); //known result;
here in place of this
public double max(List<? extends Number> list){
if you do like-
public List myList(List<? extends String> list){
list.add("string");// No, can not add anything in list except null
list.add(null);
}
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...)
Given this method:
public static <E extends Number> List<E> process(List<E> num)
I want to do this :
ArrayList<Integer> input = null ;
ArrayList<Integer> output = null ;
output = process(input);
I get an exception : Type mismatch: cannot convert from List<Integer> to ArrayList<Integer>
I know that I can have something correct like that :
List<Integer> list = new ArrayList<Integer>;
Why doesn't it work here ?
The problem is the return type. process() returns a List<E> and you're trying to stuff that into an ArrayList<E>
Your process method needs to return an implementation of the interface List. This could as well be something incompatible to ArrayList, eg. a LinkedList. The compiler notices that and forbids it.