Reputation: 5613
I read the following two links for java generics wildcards
Difference between generic type and wildcard type
and
Are wildcard generics really needed?
I still don't understand wildcards as to how this compiles,
public void foo(List<List<?>> t) {
t.add(new ArrayList<String>());
t.add(new ArrayList<Integer>());
}
but this does not,
public static void funct2(final List<?> list, final Object something) {
list.add(something); // does not compile
}
Aren't we doing the same thing in the second code block as the first?
Upvotes: 3
Views: 139
Reputation: 200148
The constructs
List<?>
and
List<List<?>>
while superficially very similar, have in fact fundamentally different semantics.
The meaning of the first construct, with a non-nested wildcard, is "a List
parameterized with some definite, but in this context unknown type".
The meaning of the second construct, with a nested wildcard, is "a List
of any kind of List
s".
This is why you can, for example, declare
class ListOfLists implements List<List<?>> {...}
but not
class ListOfSomething implements List<?> {...}
Similarly, this is legal:
List<?> xs = new ArrayList<Integer>();
but not this:
List<List<?>> lists = new ArrayList<List<Integer>>();
Based on that it should be clear that you can add any kind of list to a List<List<?>>
, but can't add an Object
to a List<?>
because it could actually be a List<String>
or any other type, and adding an Object
would clearly be illegal.
Upvotes: 1
Reputation: 11020
This is weird, and kinda hard. I'll try to explain the best I can. In the first example, the lists that you add match the pattern of the wildcard. In other words, List<List<?>>
is a list that can contain any sort of parametrized list, and that's what you add.
Explained differently: the list is parametrized, but it's still a list, and you add a List
, so that's cool.
In the second example List<?>
is a list that could contain any sort of object, but we don't know what the type of that object is. Since we don't know, you can't add any type at all safely. If the list was a List<Integer>
and something
was a String
, well that doesn't match, so no-go.
It's weird because it doesn't seem consistent. Sometimes you can add stuff, and sometimes you can't. But you have to reason out what is being requested, and why. In a pinch, the compiler will tell you. Try to work backwards from what the compiler says, it'll help you understand what you are doing wrong (or right).
Upvotes: 1
Reputation: 178253
The first example method has a List<List<?>>
parameter. The generic type parameter is List<?>
and that means "any List
of any type", so it accepts an ArrayList<String>
and an ArrayList<Integer>
. After type erasure, the JVM sees List
and ArrayList
here anyway. Note that only a List<?>
can come out of t
now, no matter what went in. But an ArrayList<String>
is a List<?>
and an ArrayList<Integer>
is a List<?>
also.
The second example method has a List<?>
parameter. The generic type parameter is a simple wildcard -- a specific yet unknown type. It could be a List<String>
, a List<Object>
, or a List<Foo>
; the compiler doesn't know. The compiler must disallow calling the add
method with anything but null
, because it can't guarantee type safety. The something
object being added to the list could be an Integer
, and list
could be a List<Foo>
for all it knows.
The difference is that in the first example, both objects being added are List
s, yet in the second example, a simple Object
is being passed to a List
that contains an unknown type. The first is allowed and the second is not allowed, as I have explained above.
The semantics of List<List<?>>
is "list of lists of any type", but the semantics of List<?>
is "list of a specific yet unknown type".
Upvotes: 3
Reputation: 566
In the second example you can only get from the list, you are not telling the compiler what kind of objects does the list receive so something
wont be accepted into List<?> list
.
You need to change it for something like:
public static <E> void funct2(final List<E> list, final E something) {
list.add(something);
}
Upvotes: 0