Which method declaration accepts limited and unlimited multi-level generics?

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 consulted with PECS , and I believe that transform() is the correct declaration of a method, but I can’t figure out for my whole life how to get one method to handle both cases.

Please note that this issue only occurs for tiered Generics. List<? extends Number> List<? extends Number> works just fine. The problem does not apply to lists. You will get it for Future<Task<X>> etc.

In which method declaration will both limited and unlimited Generics be processed? And if this is not possible, why?

+5
source share
1 answer

The most specific type that this works for is ? extends List<? extends ?> ? extends List<? extends ?> ? 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 of why this makes sense.

A List<? extends Number> List<? extends Number> is obviously not a List<E> for any E , because it does not need to insert instances of the most common E , so the first determination is not performed.

A List<Integer> corresponds to List<? extends Number> List<? extends Number> , but this does not help, because these types are still unequal, so the types List<List<Integer>> and List<List<? extends Number>> List<List<? extends Number>> not fully connected. Therefore, the second definition also fails.

Instead, you should use the smallest top border of the site List<List<Integer>> and List<List<? extends Number>> List<List<? extends Number>> . You can get it using the following rule: LUB-site-use-site List<A> and List<B> :

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

Now, step by step:

  • For A = Integer and B = ? extends Number B = ? extends Number smallest upper bound ? extends Number ? extends Number because Integer matches ? extends Number ? extends Number .
  • For List<Integer> and List<? extends Number> List<? extends Number> becomes the smallest upper bound ? extends List<? extends Number> ? extends List<? extends Number>
  • So the end result is List<? extends List<? extends Number>> List<? extends List<? extends Number>> List<? extends List<? extends Number>> .

Hooray for dispersion using the site;)

+4
source

Source: https://habr.com/ru/post/1275724/


All Articles