5

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.

Jeff Axelrod
  • 27,676
  • 31
  • 147
  • 246
Carl
  • 7,538
  • 1
  • 40
  • 64
  • 1
    how does it "not work"? what is the error message? – newacct Oct 03 '09 at 19:16
  • 1
    Looks like you have two right answers below for what's causing the problem - but I wouldn't take their advice literally. Stick with the non-wildcard version, so that the caller of the method doesn't have to work around this limitation of wildcards. – Daniel Earwicker Oct 03 '09 at 19:40
  • @newacct: compilation error, type mismatch – Carl Oct 03 '09 at 19:59
  • @Carl: i tested this on my mac os x 10.5 w/ java 5 and the error was "incompatible types required: java.util.List found: java.util.List" – non sequitor Oct 05 '09 at 03:10

2 Answers2

4

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
  • 265,237
  • 58
  • 395
  • 493
  • @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 Oct 05 '09 at 03:16
  • 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 ``. Put them together, and you get my answer. – erickson Oct 05 '09 at 04:37
2

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 andU 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
  • 38,619
  • 8
  • 86
  • 96
  • So the precedence (sort of) for type inference is from arguments first, then return type for these sort of generics. – Carl Oct 03 '09 at 20:04
  • chosen based on good explanation (which erickson also offers), plus avoiding advising the Class. structure, which I think is ugly. – Carl Oct 03 '09 at 20:06