Let\'s first consider a simple scenario (see complete source on ideone.com):
import java.util.*;
public class TwoListsOfUnknowns {
static void doNothing
As Appendix B indicates, this has nothing to do with multiple wildcards, but rather, misunderstanding what List really means.>
Let's first remind ourselves what it means that Java generics is invariant:
Integer is a NumberList is NOT a ListList IS a List extends Number>We now simply apply the same argument to our nested list situation (see appendix for more details):
List is (captureable by) a List>List>
is NOT (captureable by) a List>
List>
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 can capture types like >
List, >
List, etc. This is NOT true.>
That is, a List:>
List extends List>>Here's a snippet to illustrate the above points:
List> lolAny = new ArrayList>();
lolAny.add(new ArrayList());
lolAny.add(new ArrayList());
// lolAny = new ArrayList>(); // DOES NOT COMPILE!!
List extends List>> lolSome;
lolSome = new ArrayList>();
lolSome = new ArrayList>();
Here's yet another example with bounded nested wildcard:
List> lolAnyNum = new ArrayList>();
lolAnyNum.add(new ArrayList());
lolAnyNum.add(new ArrayList());
// lolAnyNum.add(new ArrayList()); // DOES NOT COMPILE!!
// lolAnyNum = new ArrayList>(); // DOES NOT COMPILE!!
List extends List extends Number>> lolSomeNum;
lolSomeNum = new ArrayList>();
lolSomeNum = new ArrayList>();
// lolSomeNum = new ArrayList>(); // DOES NOT COMPILE!!
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> is not
// applicable for the arguments (List)
}
public static void main(String[] args) {
List
lol.add(list); is illegal because we may have a List and a > lol
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.
(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
1…Tn> to G1…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
? extendsBi, then …- If Ti is a wildcard type argument of the form
? superBi, 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
? can CC by rule 1List extends Number> can CC List
? can CC by rule 2NumberList extends Number> can NOT CC List
? can CC by rule 2, but compile time error occurs due to incompatible typesNow let's try some nesting:
List>
can NOT CC List>
? can NOT CCList extends List>> can CC List>
? can CC by rule 2List>, which can CC List? can CCList extends List extends Number>> can CC List>
? can CC by rule 2List extends Number>, which can CC List? can CCList extends List extends Number>> can NOT CC List>
? can CC by rule 2List extends Number>, which can CC, but gives a compile time error when applied to List? can CCTo 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, ?>(); // DOES NOT COMPILE!!!
new HashMap, Set>>(); // DOES NOT COMPILE!!!
However, the following compiles just fine:
// WildSnippet2
new HashMap,Set>>(); // compiles fine!
new HashMap
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 can CC, which makes the direct instantiation through new illegal.