1

Consider the following 2 alternate APIs:

void method(List<?> list)
<T> void method(List<T> list)

I know that their internal implementation will have many differences to deal with, such as List<?> wont be able to write into the list etc.

Also, in my knowledge List<?> will allow any parameterized type with List as base type. So will be the case with List<T> also.

Can anybody tell me if there is any difference at all in what kinds of inputs these 2 APIs will accept. (NOT the 2 APIs internal implementation differences.)

Boann
  • 48,794
  • 16
  • 117
  • 146
  • I believe they'll accept exactly the same input. The limitations of `>` only appear when you try to work with the list and the type is relevant for some reason. – blalasaadri Mar 30 '15 at 10:03
  • I think `List>` only adds the information that no element will be added to the list (except for `null`s) – jamp Mar 30 '15 at 10:09

1 Answers1

3

The internal implementation is exactly the same. In fact, both methods compiled with javac will yield equal method byte code, if they compile at all).

However, during compilation, the first method is specified to not care about the component type of the list, while the second requires that the component type be invariant. This means that whenever I call such a method, the component type of list will be fixed by whatever the call site uses.

I can call my method with a List<String> and T will be synonymous to String during the call (from the perspective of the compiler). I can also call it with a List<Runnable> and T will be synonymous to Runnable during the call.

Note that your method does not return anything, but it very well could do so depending on the arguments. Consider the method:

<T> T findFirst(Collection<T> ts, Predicate<T> p) { … }

You can use this method for each T. BUT it only works if our T is equal for the collection and predicate — this is what "invariance" means. You could in fact specify the method to be applicable in more contexts:

<T> T findFirst(Collection<? extends T> ts, Predicate<? super T> p) { … }

This method would work the same as above, but it would be more lenient in what types it accepts. Consider a type hierarchy A extends B extends C. Then you could call:

Collection<A> cs = …;
Predicate<C> p = …;
B b = findFirst(cs, p);

We call the type of ts covariant and the type of p (in the method signature) contravariant.

Wildcards (?) are a different matter. They can be bounded (like in our cases above) to be co- or contravariant. If they are unbounded, the compiler actually needs a concrete type to fill in at compile time (which is why you will sometimes get errors like "type wildcard-#15 is not a match for wildcard-#17"). The specific rules are laid out in the Java Language Specification, Section 4.5.1.

llogiq
  • 13,815
  • 8
  • 40
  • 72
  • 1
    Could you please elaborate a bit more on your 2nd paragraph? –  Mar 30 '15 at 10:16
  • I just extended my answer. – llogiq Mar 30 '15 at 12:22
  • Thanks but, You are not not talking about List> specifically yet. In my understanding all the things you told about List above will still be applicable to List> from a caller's point of view. I was looking for the differences as seen by the caller. –  Mar 30 '15 at 13:19