Reputation: 2480
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
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
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