17

In the following example:

public static void main(String[] args) {

    List<String> b = new ArrayList<String>();
    first(b);
    second(b);

    List<List<String>> a = new ArrayList<List<String>>();
    third(a);
    fourth(a);  // doesnt work

}

private static <T> void first(List<T> a){
    System.out.println("List of T");
}

private static void second(List<?> a){
    System.out.println("List of anything ");
}

private static <T> void third(List<List<T>> a){
    System.out.println("List of a List of T ");
}

private static void fourth(List<List<?>> a){
    System.out.println("List of a List of anything ");
}

Why does the call to second(b) work, but the call to fourth(a) doesn't ?

I get the following error:

The method fourth(List<List<?>>) in the type `TestTest` is not applicable for the arguments (`List<List<String>>`)
jbowes
  • 3,764
  • 19
  • 37
why
  • 1,177
  • 3
  • 11
  • 22

5 Answers5

16

If you want to be able to call fourth with a List<List<String>> argument, then you'll need to change your signature to this:

private static void fourth(List<? extends List<?>> a){
    System.out.println("List of a List of anything ");
}

The above will work because unlike List<List<?>>, List<? extends List<?>> is compatible with List<List<String>>. Think of it this way:

List<List<String>> original = null;
List<? extends List<?>> ok  = original; // This works
List<?> ok2                 = original; // So does this
List<List<?>> notOk         = original; // This doesn't

List<Integer> original      = null;
List<? extends Number> ok   = original; // This works
List<?> ok2                 = original; // So does this
List<Number> notOk          = original; // This doesn't

The reasoning is simple. If you had

private static void fourth(List<List<?>> a) {
    List<?> ohOh = Arrays.asList(new Object());
    a.add(ohOh);
}

And then if you could call that method as such:

List<List<String>> a = new ArrayList<List<String>>();
fourth(a);
String fail = a.get(0).get(0); // ClassCastException here!
Lukas Eder
  • 181,694
  • 112
  • 597
  • 1,319
  • 1
    +1 liked the answer but more explanation is required why `? extends List>` works? – Amit Deshpande Nov 05 '12 at 10:56
  • Spot-on answer Lukas! But I believe the last line should read `String fail = a.get(0).get(0)`. – Saintali Nov 05 '12 at 11:57
  • 1
    Why is List extends List>> a super-type of List>? – why Nov 05 '12 at 14:07
  • @user1799796: I'm not sure if I got the terms right. The types are certainly compatible, though. I have added an analogy, which isn't quite the same thing, but similar. It may feel more intuitive, when you have a `List extends Number>`, rather than a `List extends List>>`. – Lukas Eder Nov 05 '12 at 15:04
  • @LukasEder Thanks for your explanation and the analogy - it helped me alot. – why Nov 05 '12 at 19:55
  • So basically if you have `class A`, and a variable `A> anObj = ...` (or `A extends whatever>`), you can't invoke any methods on `anObj` that have arguments of type `T` in the declaration, right? – Andy May 20 '13 at 21:14
  • 1
    @Andy: Almost. You can always pass the `null` literal, as `null` is a valid value for `?`. Try `List> list = new ArrayList(); list.add(null);` – Lukas Eder May 21 '13 at 09:27
5

A List<List<String>> isn't a List<List<?>>.

You should be able to put any List<?> into a List<List<?>>, no matter what the ?. A List<List<String>> will only accept a List<String>.

artbristol
  • 30,694
  • 5
  • 61
  • 93
2

This implies that the type is unknown and objects of any type can be added to List<List<?>> that are heterogeneous and compiler cannot guarantee that all object in List<List<?>> are of same type. Hence it cannot be passed to new ArrayList<List<String>>() that takes a bounded type as parameter.

Amit Deshpande
  • 18,407
  • 4
  • 42
  • 69
hoang nguyen
  • 1,727
  • 4
  • 19
  • 20
1

List<List<String>> is not same as List<List<?>>. Generics are invariant in nature. If you only do List<?> and pass List<String> then it will work because List of Anything can be represented by Lists of String.

But List of List of anything can not be represented by List of List of String.

@Lukas Elder has already specified case that will work. Here is the second case that will work

private static void fourth(List<?> a){
    System.out.println("List of anything ");
}
Community
  • 1
  • 1
Amit Deshpande
  • 18,407
  • 4
  • 42
  • 69
  • So your saying there is no flexibility in Java generic type parameters? When a type parameter is used within a generic the instantiated object expects exactly the type specified when the generic type parameter is used? – Kevin Bowersox Nov 05 '12 at 10:34
  • @kmb385 I must admit I have failed to interpret meaning of the comment.Flexibility in Generic types using `wild cards` , `bounded wind cards` or `bounded types`. – Amit Deshpande Nov 05 '12 at 10:43
0
List<List<?>> == List {                 //That contains any unknown type lists
                        List<Integer>,
                        List<String>,
                        List<Object>
                      }

Where as

List<? extends List<?> == List {       //That contains same unknown type lists
                        List<Integer>,
                        List<Integer>,
                        List<Integer>
                      }

So here

 List<List<String>> == List {        //That contains same String lists
                        List<String>,
                        List<String>,
                        List<String>
                      }

Hence List<? extends List<?> is super type of List<List<String>> and assignable.

So valid value to call your fourth method is below.

    List<List<?>> a1 =  new ArrayList<List<?>>();
    a1.add(new ArrayList<String>());
    a1.add(new ArrayList<Integer>());
    a1.add(new ArrayList<Object>());
Kanagavelu Sugumar
  • 16,614
  • 19
  • 77
  • 92