5

Given:

public class Testcase {

    public static <E> List<List<E>> transform(List<List<E>> list) {
        return list;
    }

    public static <E> List<List<? extends E>> transform2(List<List<? extends E>> list) {
        return list;
    }

    public static void main(String[] args) {
        List<List<Integer>> known = new ArrayList<>();
        List<List<? extends Number>> unknown = new ArrayList<>();

        transform(known);    // works
        transform(unknown);  // fails

        transform2(known);   // fails
        transform2(unknown); // works
    }
}

The compiler accepts transform(known) but complains:

cannot infer type-variable(s) E
    (argument mismatch; List<List<? extends Number>> cannot be converted to List<List<E>>)
  where E is a type-variable:
    E extends Object declared in method <E>transform(List<List<E>>)

for transform(unknown). I get the opposite problem for transform2(). I've consulted PECS and I believe that transform() is the correct method declaration but I can't for the life of my figure out how to get a single method to handle both cases.

Note that this problem only occurs for multi-level Generics. List<? extends Number> works just fine. The problem is not specific to Lists. You'll get it for Future<Task<X>> and so on.

What method declaration will handle both bounded and unbounded Generics? And if it's not possible, why?

Gili
  • 86,244
  • 97
  • 390
  • 689

1 Answers1

4

The most specific type for which this works seems to be ? extends List<? extends ?>:

class Testcase {

    public <E> List<List<E>> transform(List<List<E>> list) {
        return list;
    }

    public <E> List<List<? extends E>> transform2(List<List<? extends E>> list) {
        return list;
    }

    public <E> List<? extends List<? extends E>> transform3(List<? extends List<? extends E>> list) {
        return list;
    }    

    public void test(String[] args) {
        List<List<Integer>> known = new ArrayList<>();
        List<List<? extends Number>> unknown = new ArrayList<>();

        transform(known);    // works
        // transform(unknown);  // fails

        // transform2(known);   // fails
        transform2(unknown); // works

        transform3(known);
        transform3(unknown);
    }
}

Here is an explanation why this makes sense.

A List<? extends Number> is clearly not a List<E> for any E, because it doesn't have to be able to insert instances of the most general E, thus the first definition fails.

A List<Integer> conforms to List<? extends Number>, but this does not help, because these types are still unequal, therefore types List<List<Integer>> and List<List<? extends Number>> are completely unrelated. Therefore, the second definition also fails.

What you want instead is the use-site least upper bound of List<List<Integer>> and List<List<? extends Number>>. You can obtain it using the following rule: the use-site-LUB of List<A> and List<B> is:

USLUB(List<A>, List<B>) = List<? extends USLUB(A, B)>

Now, step by step:

  1. For A = Integer and B = ? extends Number, the least upper bound is ? extends Number, because Integer conforms to ? extends Number.
  2. For List<Integer> and List<? extends Number> the least upper bound becomes ? extends List<? extends Number>
  3. Thus the final result is List<? extends List<? extends Number>>.

Hurray for use-site variance ;)

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • Ouch. My brain hurts. Any idea what why this works while the originals fail? – Gili Mar 06 '18 at 05:43
  • @Gili Yeah, that indeed hurts... But I've added a somewhat detailed explanation how to derive this funny type, I hope that it hurts a little less once you see the simple rule behind it ;) – Andrey Tyukin Mar 06 '18 at 05:55
  • I don't understand one thing about your explanation. Why is `? extends List extends Number>` considered an upper-bound of `List`? I understand why `? extends List` would act as an upper-bound but not the former. – Gili Mar 06 '18 at 06:04
  • @Gili `Integer` conforms to `? extends Number`. Therefore, `List` is a special case of `List extends Number>`. In particular, `List` conforms to `? extends List extends Number>`. The last statement is just a weakening of the stronger preceeding statement. – Andrey Tyukin Mar 06 '18 at 06:07
  • Got it. Thank you for the step-by-step explanation. – Gili Mar 06 '18 at 06:08