I am trying to understand java generics and they seem extremely difficult to understand. For example, this is fine...
public class Main {
public static void main(String[] args) {
List<?> list = null;
method(list);
}
public static <T> void method(List<T> list) { }
}
... as is this...
public class Main {
public static void main(String[] args) {
List<List<?>> list = null;
method(list);
}
public static <T> void method(List<T> list) { }
}
... and this ...
public class Main {
public static void main(String[] args) {
List<List<List<?>>> list = null;
method(list);
}
public static <T> void method(List<List<T>> list) { }
}
... but this doesn't compile:
public class Main {
public static void main(String[] args) {
List<List<?>> list = null;
method(list);
}
public static <T> void method(List<List<T>> list) { }
}
Can someone explain what is going on in simple language?
The question mark (?) is known as the wildcard in generic programming. It represents an unknown type. The wildcard can be used in a variety of situations such as the type of a parameter, field, or local variable; sometimes as a return type.
In a generic type or method definition, a type parameter is a placeholder for a specific type that a client specifies when they create an instance of the generic type.
In the Java programming language, the wildcard ? is a special kind of type argument that controls the type safety of the use of generic (parameterized) types. It can be used in variable declarations and instantiations as well as in method definitions, but not in the definition of a generic type.
The main thing to understand with generic types, is that they aren't covariant.
So whilst you can do this:
final String string = "string";
final Object object = string;
The following will not compile:
final List<String> strings = ...
final List<Object> objects = strings;
This is to avoid the situations where you circumvent the generic types:
final List<String> strings = ...
final List<Object> objects = strings;
objects.add(1);
final String string = strings.get(0); <-- oops
So, going through your examples one by one
Your generic method takes a List<T>
, you pass in a List<?>
; which is (essentially) a List<Object>
. T
can be assigned to the Object
type and the compiler is happy.
Your generic method is the same, you pass in a List<List<?>>
. T
can be assigned to the List<?>
type and the compiler is again happy.
This is basically the same as 2 with another level of nesting. T
is still the List<?>
type.
Here is where it goes a little pear shaped, and where my point from above comes in.
Your generic method takes a List<List<T>>
. You pass in a List<List<?>>
. Now, as generic types are not covariant, List<?>
cannot be assigned to a List<T>
.
The actual compiler error (Java 8) is:
required:
java.util.List<java.util.List<T>>
found:java.util.List<java.util.List<?>>
reason: cannot infer type-variable(s)T
(argument mismatch;java.util.List<java.util.List<?>>
cannot be converted tojava.util.List<java.util.List<T>>
)
Basically the compiler is telling you that it cannot find a T
to assign because of having to infer the type of the List<T>
nested in the outer list.
Lets look at this in a little more detail:
List<?>
is a List
of some unknown type - it could be a List<Integer>
or a List<String>
; we can get
from it as Object
, but we cannot add
. Because otherwise we run into the covariance issue I mentioned.
List<List<?>>
is a List
of List
of some unknown type - it could be a List<List<Integer>>
or a List<List<String>>
. In case 1 it was possible to assign T
to Object
and just not allow add
operations on wildcard list. In case 4 this cannot be done - primarily because there is not a generics construct to prevent add
to the outer List
.
If the compiler were to assign T
to Object
in the second case then something like the following would be possible:
final List<List<Integer>> list = ...
final List<List<?>> wildcard = list;
wildcard.add(Arrays.asList("oops"));
So, due to covariance, it is not possible to assign a List<List<Integer>>
to any other generic List
safely.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With