tags:

views:

239

answers:

3

Consider this code:

public <T> List<T> meth(List<?> type)
{
   System.out.println(type); // 1
   return new ArrayList<String>(); // 2
}

It does not compile at line 2, saying that List is required.

Now, if it's changed to:

public <T> List<?> meth(List<T> type)
{
   System.out.println(type); // 1
   return new ArrayList<String>(); // 2
}

It does compile. Why? I thought the difference between declaring a generic type with T and using the wildcard was that, when using the wildcard, one cannot add new elements to a collection. Why would <?> allow a subtype of List to be returned? I'm missing something here, what's the explicit rule and how it's being applied?

+1  A: 

In the first case, T is not necessarily a superclass of String. If you choose a T like Integer and call the method, it'll fail; so it won't compile. However, the second will compile as surely, any ArrayList<String> is a valid List of something.

Mehrdad Afshari
+4  A: 

The difference is in the return type declaration. List<String> is not a subtype of List<T>, but it is a subtype of List<?>.

List<?> makes no assumptions regarding its type variable, so the following statements are valid:

List<?> l0 = new ArrayList<String>();
List<?> l1 = new ArrayList<Object>();
List<? extends Number> ltemp = null;
List<?> l2 = ltemp;

List<T> assumes that the type argument will be resolved in the context of client (e.g. type use), when you declared it as List<String> or List<Object>. Within the method body, you cannot make any assumptions about it either.

notnoop
I don't think the last compiles.
Tom Hawtin - tackline
Thanks. Corrected it.
notnoop
+1  A: 

As said earlier, String isn't a subtype of T, so it's why it does not work. However, this code works :

public <T> List<T> meth(List<?> type)
{
   System.out.println(type); // 1
   return new ArrayList<T>(); // 2
}

and is more in the idea of what you want, I think.

Valentin Rocher