3

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?

Reinstate Monica
  • 2,420
  • 14
  • 23
  • 1
    A minor remark: since generics are invariant, you should use ` extends E>` for your `take(...)`-method parameters (`final Generic extends E> x`) to comply with [PECS](https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super). – Turing85 Apr 06 '18 at 17:35
  • @Turing85 I agree in real code but I'm asking a question about this specific code. Changing the type of `take`'s parameter in the second example to `Generic>` makes the compiler happy. But I would like to understand why. – Reinstate Monica Apr 06 '18 at 17:38
  • This is the reason I wrote it as a remark ;) Actually, to really comply with PECS, you would write `Generic extends Generic extends E>>`. As to WHY it is that the compiler rejects this... I am not fully sure. – Turing85 Apr 06 '18 at 17:40
  • Related: https://stackoverflow.com/questions/49685216/cant-add-generic-list-to-generic-map – VGR Apr 06 '18 at 18:12

2 Answers2

4

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.

user2357112
  • 260,549
  • 28
  • 431
  • 505
0

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.

GioAgu
  • 59
  • 6
  • I think the meat of the question is _why_ `Generic` and `Generic>` have no subtyping relationship in light of the latter being assignable to the former. – Reinstate Monica Apr 06 '18 at 17:49