Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
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 aNumber
- A
List<Integer>
is NOT aList<Number>
- A
List<Integer>
IS aList<? 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) aList<?>
- A
List<List<String>>
is NOT (captureable by) aList<List<?>>
- A
List<List<String>>
IS (captureable by) aList<? 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<?>>
- ... that would be a
- 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 CCList<String>
- The
?
can CC by rule 1
- The
List<? extends Number>
can CCList<Integer>
- The
?
can CC by rule 2 - In applying rule 2, Bi is simply
Number
- The
List<? extends Number>
can NOT CCList<String>
- The
?
can CC by rule 2, but compile time error occurs due to incompatible types
- The
Now let's try some nesting:
List<List<?>>
can NOT CCList<List<String>>
- Rule 4 applies, and CC is not recursive, so the
?
can NOT CC
- Rule 4 applies, and CC is not recursive, so the
List<? extends List<?>>
can CCList<List<String>>
- The first
?
can CC by rule 2 - In applying rule 2, Bi is now a
List<?>
, which can CCList<String>
- Both
?
can CC
- The first
List<? extends List<? extends Number>>
can CCList<List<Integer>>
- The first
?
can CC by rule 2 - In applying rule 2, Bi is now a
List<? extends Number>
, which can CCList<Integer>
- Both
?
can CC
- The first
List<? extends List<? extends Number>>
can NOT CCList<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 toList<Integer>
- Both
?
can CC
- The first
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.
Generic method vs wildcard - compilation error
Confusions do come when you deal with multi-level wildcard syntax. Let's understand what those types exactly mean in there:
List<List<?>>
is a concrete parameterized type. It is a heterogenous collection of different types ofList<E>
. SinceList<?>
represent a family of all the instantiation ofList
, you can't really pass anArrayList<List<String>>
toList<List<?>>
. Because, nothing stops you from adding aList<Integer>
to it inside the method, and that will crash at runtime, had compiler allowed it.List<? extends List<?>>
is a wildcard parameterized type. It represents a family of different types ofList<E>
. Basically, it might be aList<ArrayList<String>>
,List<LinkedList<Date>>
, so on. It can be a list of any type that extend from aList<?>
. So, it will be safe to pass aArrayList<List<String>>
to it, the reason being, you won't be allowed to add anything, butnull
to the list. Adding anything to the list will be a compile time error.As for
List<List<T>>
, it is again a concrete parameterized type. And since you're dealing with a generic method now, the type parameter will be inferred to be the type that is passed for it. So, for anArrayList<List<String>>
, typeT
will be inferred asT
. A generic method deals with the types that are declared with it. So, there is only a single typeT
here. All the lists you get out ofList<List<T>>
will certainly be aList<T>
for any typeT
. So, it's a homogenous collection of that type ofList
. Inside the method, you can not add any arbitraryList<E>
to theList<List<T>>
, because the compiler doesn't know whether that typeE
is compatible withT
or not. So, it is safe invocation.
Related:
- Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
- Java HashMap nested generics with wildcards
- What are multi-level wild cards? Confusion in syntax
- When to use generic methods and when to use wild-card?
Java: Wildcard Types Mismatch Results in Compilation Error
Use the following:
IManager<IAnotherRandomType<?>> test3Manager =
Factory.<IAnotherRandomType<?>>createManager(test3);
This is just a case of the compiler's type inference falling on its face, so it's necessary explicitly provide the type argument for T
.
More technically:
test3
is declared to have the type IAnotherRandomType<?>
, where ?
is a wildcard capture - a sort of one-use type parameter representing some specific unknown type. That's what the compiler's referring to when it says capture#1-of ?
. When you pass test3
into createManager
, T
gets inferred as IAnotherRandomType<capture#1-of ?>
.
Meanwhile, test3Manager
is declared to have the type IManager<IAnotherRandomType<?>>
, which has a nested wildcard - it does not behave like a type parameter but rather represents any type.
Since generics aren't covariant, the compiler can't convert from IManager<IAnotherRandomType<capture#1-of ?>>
to IManager<IAnotherRandomType<?>>
.
More reading on nested wildcards:
- Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
- Java Generic List<List<? extends Number>>
- Which super-subset relationships exist among wildcards?
Java Generics: unbounded wildcard generalization not working past second level
Note that List<B>
is not a subtype of List<A>
even if B
is a subtype of A
. Therefore, List<Class<? extends Integer>>
is not a subtype of List<Class<?>>
even though you can assign a Class<? extends Integer>
object to a Class<?>
variable. Just consider that in bar
, it would be legal to invoke a.add(Object.class)
, as Class<Object>
is a subtype of Class<?>
.
You therefore want to extend the bar
argument type to List<? extends Class<?>>
.
Passing any concrete class for a wildcard in a generic class gives error. Why?
Use wildcard for Entry
object also, wildcard represents an unknown type. In the above code you defined Entry<Integer, ?>
entry with key Integer
and value unknown type
, But in the same way you have to say for entry objects in List
static void bucketSort(List<? extends Entry<Integer, ?>> list, int n)
{
//some code
}
Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
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 aNumber
- A
List<Integer>
is NOT aList<Number>
- A
List<Integer>
IS aList<? 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) aList<?>
- A
List<List<String>>
is NOT (captureable by) aList<List<?>>
- A
List<List<String>>
IS (captureable by) aList<? 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<?>>
- ... that would be a
- 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 CCList<String>
- The
?
can CC by rule 1
- The
List<? extends Number>
can CCList<Integer>
- The
?
can CC by rule 2 - In applying rule 2, Bi is simply
Number
- The
List<? extends Number>
can NOT CCList<String>
- The
?
can CC by rule 2, but compile time error occurs due to incompatible types
- The
Now let's try some nesting:
List<List<?>>
can NOT CCList<List<String>>
- Rule 4 applies, and CC is not recursive, so the
?
can NOT CC
- Rule 4 applies, and CC is not recursive, so the
List<? extends List<?>>
can CCList<List<String>>
- The first
?
can CC by rule 2 - In applying rule 2, Bi is now a
List<?>
, which can CCList<String>
- Both
?
can CC
- The first
List<? extends List<? extends Number>>
can CCList<List<Integer>>
- The first
?
can CC by rule 2 - In applying rule 2, Bi is now a
List<? extends Number>
, which can CCList<Integer>
- Both
?
can CC
- The first
List<? extends List<? extends Number>>
can NOT CCList<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 toList<Integer>
- Both
?
can CC
- The first
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.
Double wildcard parameterization (nested wildcards) with Java generics
Use the following instead:
Set<? extends Class<?>> subTypes = reflections.getSubTypesOf(type);
The problem is that nested wildcards don't perform type capture. Declaring a Set<Class<?>>
means "a set of classes of any type", while what's being returned is a Set<Class<? extends capture#19-of ?>>
, which means "a set of classes of any type extending from some specific unknown type". In this case, that "specific unknown type" is derived from the type argument to T
, which is inferred from type
to be an unbounded wildcard capture (the ?
in Class<?>
).
For example, pretend that "specific unknown type" is Number
:
Class<Number> type = ...
Set<Class<?>> subTypes = reflections.getSubTypesOf(type);
Here, getSubTypesOf
returns a Set<Class<? extends Number>>
. That type is not assignable to Set<Class<?>>
because generic types aren't covariant. But, wildcard capture helps us express covariance, allowing types like Set<? extends Class<?>>
. The caveat is that we can't add anything but null
to such a set, since we don't know its specific type.
Related posts:
- Java Generic List<List<? extends Number>>
- Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
- What is PECS (Producer Extends Consumer Super)?
Similar posts:
- Java: Wildcard Types Mismatch Results in Compilation Error
- Issue with declaration of Map<String,Class<? extends Serializable>>
- Bounded-wildcard related compiler error
Nested wildcards
It is important to understand the implication of the wildcard types.
You already understood that you can assign your Map<Integer, Map<Integer, String>>
to Map<?, ?>
as Map<?, ?>
implies arbitrary types, unknown to whoever might have a reference of the declared type Map<?, ?>
. So you can assign any map to Map<?, ?>
.
In contrast, if you have a Map<?, Map<?, ?>>
it has an unknown key type but the value type is not unknown. It’s Map<?,?>
the type, recall the information above, that can be assigned with any map.
So, the following code is legal:
Map<?, Map<?, ?>> map=new HashMap<>();
map.put(null, Collections.<String,String>singletonMap("foo", "bar"));
map.put(null, Collections.<Double,Integer>singletonMap(42.0, 1000));
map.put(null, Collections.<Object,Boolean>singletonMap(false, true));
Here, we are putting a null
key as we can’t put
anything else for keys but arbitrary typed maps as values as that’s what a value type of Map<?, ?>
implies: can be assigned from arbitrary maps. Note that by iterating over the entries we can also set other entries having non-null
keys to arbitrary maps then.
So I’m quite sure that you don’t want to assign your Map<Integer, Map<Integer, String>>
to a Map<?, Map<?, ?>>
and discover arbitrary maps not being Map<Integer, String>
as values afterwards and that you are quite happy that the compiler doesn’t allow this.
What you actually want to do is to assign your map to a type which has both, key and value type, unknown but still telling that your values are maps:
Map<Integer, Map<Integer, String>> someMap = new HashMap<>();
Map<?, ? extends Map<?, ?>> map=someMap;
In the generic type system Map<Integer, String>
is a sub-type of Map<?, ?>
so you can assign it to Map<?, ?>
as well as ? extends Map<?, ?>
. This sub-type relationship is not different than the relationship of String
to Object
. You can assign any String
to a variable of type Object
but if you have a Map<?,String>
you can’t assign it to Map<?,Object>
but only to Map<?, ? extends Object>
for the same reason: the map shall continue to contain String
s as values rather than receiving arbitrary objects.
Note that you can workaround this limitation. You can say:
Map<Integer, Map<Integer, String>> someMap = new HashMap<>();
Map<?, Map<?, ?>> map=Collections.unmodifiableMap(someMap);
Since the map returned by unmodifiableMap
does not allow any modifications, it allows widening the key and value types. The contained values are of the specified type (i.e. Map<?, ?>
) when you query the map, but attempts to put in arbitrary map values, while not rejected by the compiler, will be rejected at runtime.
Related Topics
How to Call the Default Deserializer from a Custom Deserializer in Jackson
How to Deploy a Javafx 11 Desktop Application with a Jre
Java.Lang.Classnotfoundexception: Com.Mysql.Jdbc.Driver in Eclipse
How to Import a Class from Default Package
Look and Feel Is Not Updating in Swing Jtabbedpane
Why Is Spring's Applicationcontext.Getbean Considered Bad
Compiling a Java Program into an Executable
How to See Jit-Compiled Code in Jvm
Connecting to Remote Url Which Requires Authentication Using Java
Java Error: Implicit Super Constructor Is Undefined for Default Constructor
Illegalmonitorstateexception on Wait() Call
Java: Define Terms Initialization, Declaration and Assignment
How to Convert Byte Size into a Human-Readable Format in Java
Maven Does Not Find Junit Tests to Run