1

I had a issue where (to simplify):

public void method(List<List<?>> list){...}

gave me a compilation error when called with:

method(new ArrayList<List<String>>()); // This line gives the error

After reading a similar thread, I understood that it would work if I were to rewrite the method signature as:

public void method(List<? extends List<?>> list){...}

Now, my question is, why does the following work then?

public <T> void method(List<List<T>> list){...}
Community
  • 1
  • 1
Deiwin
  • 420
  • 5
  • 21
  • possible duplicate of [Java Generics ? , E and T what is the difference?](http://stackoverflow.com/questions/6008241/java-generics-e-and-t-what-is-the-difference) – kosa Oct 28 '13 at 19:28
  • @Nambari That is not really a duplicate. – Rohit Jain Oct 28 '13 at 19:38
  • @RohitJain: I will remove it if required, but accepted answer there covers everything related to Generics (AFAIK). OP posted answer below is from one of the references. – kosa Oct 28 '13 at 19:42
  • @Nambari Yes, the accepted answer does give links to all the good tutorials and references on generics, but it doesn't specifically talk about multi-level wildcards, which is what OP's case is. – Rohit Jain Oct 28 '13 at 19:54
  • related: [Multiple wildcards on a generic methods makes Java compiler (and me!) very confused](http://stackoverflow.com/questions/3546745/multiple-wildcards-on-a-generic-methods-makes-java-compiler-and-me-very-confu) – Paul Bellora Oct 28 '13 at 20:07

3 Answers3

5

Confusions do come when you deal with multi-level wildcard syntax. Let's understand what those types exactly mean in there:

  • List<List<?>> is a concrete parameterized type. It is a heterogenous collection of different types of List<E>. Since List<?> represent a family of all the instantiation of List, you can't really pass an ArrayList<List<String>> to List<List<?>>. Because, nothing stops you from adding a List<Integer> to it inside the method, and that will crash at runtime, had compiler allowed it.

  • List<? extends List<?>> is a wildcard parameterized type. It represents a family of different types of List<E>. Basically, it might be a List<ArrayList<String>>, List<LinkedList<Date>>, so on. It can be a list of any type that extend from a List<?>. So, it will be safe to pass a ArrayList<List<String>> to it, the reason being, you won't be allowed to add anything, but null to the list. Adding anything to the list will be a compile time error.

  • As for List<List<T>>, it is again a concrete parameterized type. And since you're dealing with a generic method now, the type parameter will be inferred to be the type that is passed for it. So, for an ArrayList<List<String>>, type T will be inferred as T. A generic method deals with the types that are declared with it. So, there is only a single type T here. All the lists you get out of List<List<T>> will certainly be a List<T> for any type T. So, it's a homogenous collection of that type of List. Inside the method, you can not add any arbitrary List<E> to the List<List<T>>, because the compiler doesn't know whether that type E is compatible with T or not. So, it is safe invocation.


Related:

Community
  • 1
  • 1
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
2

I think I found the answer in Angelika Langer's generics FAQ, "Case Study #3":

If a method signature uses multi-level wildcard types then there is always a difference between the generic method signature and the wildcard version of it. Here is an example. Assume there is a generic type Box and we need to declare a method that takes a list of boxes.

Example (of a method with a type parameter):

public static  <T> void print1( List <Box<T>> list) { 
  for (Box<T> box : list) { 
    System.out.println(box); 
   } 
} 

Example (of method with wildcards):

public static void print2( List <Box<?>> list) { 
  for (Box<?> box : list) { 
    System.out.println(box); 
  } 
} 

Both methods are perfectly well behaved methods, but they are not equivalent. The generic version requires a homogenous list of boxes of the same type. The wildcard version accepts a heterogenous list of boxes of different type. This becomes visible when the two print methods are invoked.

Community
  • 1
  • 1
Deiwin
  • 420
  • 5
  • 21
1

The basic reason is that List<List<?>> is not a superclass of List<List<String>>.

A List<List<?>> could contain a List<Integer> and a List<String> for example.

The generic types must match exactly, otherwise you could get erroneous assignments made.

Bohemian
  • 412,405
  • 93
  • 575
  • 722