Reinstate Monica
Reinstate Monica

Reputation: 2480

Why can't a nested wildcard be assigned to a variable with a type parameter?

Consider this example:

static class Generic<E> {}

static void run() {
    Generic<?> x = null;
    take(x);
}

static <E> void take(final Generic<E> x) {}

In run, the wildcard in the type of x represents some unknown type T. The E parameter of the take method can be assigned any type. The compiler infers that it's safe to assign T to E.


In the second example we nest the wildcard one level deeper:

static void run() {
    Generic<Generic<?>> x = null;
    take(x);
}

static <E> void take(final Generic<Generic<E>> x) {}

In this example the wildcard still represents some unknown type T and the E parameter can be assigned any type. Why does the compiler not infer that T can be assigned to E and allow this code to compile? Is there a principled reason for this code not compiling?

Note: changing the take method in the second example to the below compiles:

static <E> void take(final Generic<Generic<? extends E>> x) {}

Is there an essential difference between Generic<Generic<E>> and Generic<Generic<? extends E>> when E is unbounded?

Upvotes: 3

Views: 322

Answers (2)

user2357112
user2357112

Reputation: 280207

Generic<Generic<?>> does not mean Generic<Generic<T>> for an unknown T. It means the specific type Generic<Generic<?>>. An object of type Generic<Generic<?>> is never of type Generic<Generic<T>> for any T.

For example, if you had List<List<?>>, this would be a list that can take List<String>, List<Integer>, List<Object>, etc. as elements. There is no type T for which List<List<T>> can do that.

Intuitively, the first snippet calls take<T> for some unknown T, while the second snippet would need to specifically call take<?> with ? as the type parameter, which is forbidden. Concretely, I believe Java generates a fresh type variable for the ? on Generic<?> in capture conversion for the first snippet, which doesn't happen for the nested wildcard in the second snippet. I haven't fully figured out how the Java Language Specification says the type inference plays out, though.

Upvotes: 4

GioAgu
GioAgu

Reputation: 59

I am not sure but I think it is also for the Liskov principle. A generic type is a generic class or interface that is parameterized over types. The Liskov substitution principle doesn't work with the parameterized types. For example is a compile time error if you write:

List<Numbers> list = new ArrayList<Integers>();

The same applies to wildcards: when evaluating Generic<Generic<E>> with Generic<Generic<?>> the Liskov principle tells you that Generic<E> and Generic<?> have not a subtyping relationship. That's why it gives a compile time error, because it cannot infer nothing from ? with E.

Upvotes: 0

Related Questions