views:

167

answers:

2

I have an interface A, which class B implements.

The following generic method works

public static <T, U extends T> List<T> listFactory(Collection<U> source) {
 return new ArrayList<T>(source);
}

but

public static <T> List<T> listFactory(Collection<? extends T> source) {
 return new ArrayList<T>(source);
}

does not (compilation error, type mismatch), when I am directing the output into

List<A> tester = listFactory(B.defaultCollectionFactory(3));

defaultCollectionFactory(int count) statically provides a collection of Bs, with a default labeling scheme.

Any insights as to why that is? It seems like the generic U and wildcard are doing the same thing.

+2  A: 

In the first construct, you are specifying that you are returning a List of the interface of the item that was passed in. You specify the relationship between the passed in Object and the return Object type in the U extends T direction. In this case, the compiler can associate A and B with T and U respectively.

In the second, there is no such differentiation, so the compiler assumes that T refers to B and will type the return value as List<B>. You then fall into the trap where, although B is an instance of A, List<B> is not an instance of List<A>. The compiler will complain:

Type mismatch: cannot convert from List<B> to List<A>

You will find that, with the first construct, you have the liberty of specifying a List of any interface the B implements or any superclass in the B hierarchy (List<Object>, for example), and the compiler will not complain.

akf
So the precedence (sort of) for type inference is from arguments first, then return type for these sort of generics.
Carl
chosen based on good explanation (which erickson also offers), plus avoiding advising the Class.<T> structure, which I think is ugly.
Carl
+2  A: 

The compiler is inferring a different type parameter for the listFactory method than you expect. It infers that T is type B, so the signature is effectively List<B> listFactory(Collection<? extends B> source). Specify the type parameter A by being explicit in the method invocation:

List<A> tester = Test.<A> listFactory(B.defaultCollectionFactory(3));
erickson
@erikson: This static call on a parameterized type actually works, doesn't make sense to me, can you break it down with an example and theory explanation, thanks.
non sequitor
Any static method can (really, should) be qualified by the class to which it belongs. The OP didn't specify what it is; I used "Test". So that would give you a normal static method call: `Test.listFactory(...)`. Then, the type arguments for any parameterized method can be specified explicitly, by adding them before the method name. That's the `<A>`. Put them together, and you get my answer.
erickson