Ashay Batwal
Ashay Batwal

Reputation: 5613

Java wildcards confusing example

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

Answers (4)

Marko Topolnik
Marko Topolnik

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 Lists".

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

markspace
markspace

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

rgettman
rgettman

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 Lists, 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

j2e
j2e

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

Related Questions