GhostCat GhostCat - 4 days ago 6
Java Question

Java generics: wildcard<?> vs type parameter<E>?

I am refreshing my knowledge on Java generics. So I turned to the excellent tutorial from Oracle ... and started to put together a presentation for my coworkers. I came across the section on wildcards in the tutorial that says:


Consider the following method, printList:

public static void printList(List<Object> list) {
...


The goal of printList is to print a list of any type, but it fails to achieve that goal — it prints only a list of Object instances; it cannot print
List<Integer>
,
List<String>
,
List<Double>
, and so on, because they are not subtypes of
List<Object>
. To write a generic printList method, use
List<?>
:

public static void printList(List<?> list) {



I understand that
List<Object>
will not work; but I changed the code to

static <E> void printObjects(List<E> list) {
for (E e : list) {
System.out.println(e.toString());
}
}
...
List<Object> objects = Arrays.<Object>asList("1", "two");
printObjects(objects);
List<Integer> integers = Arrays.asList(3, 4);
printObjects(integers);


And guess what; using
List<E>
I can print different types of Lists without any problem.

Long story short: at least the tutorial indicates that one needs the wildcard to solve this problem; but as shown, it can be solved this way too. So, what am I missing?!

(side note: tested with Java7; so maybe this was a problem with Java5, Java6; but on the other hand, Oracle seems to do a good job regarding updates of their tutorials)

Answer

Your approach of using a generic method is strictly more powerful than a version with wildcards, so yes, your approach is possible, too. However, the tutorial does not state that using a wildcard is the only possible solution, so the tutorial is also correct.

What you gain with the wildcard in comparison to the generic method: You have to write less and the interface is "cleaner" since a non generic method is easier to grasp.

Why the generic method is more powerful than the wildcard method: You give the parameter a name which you can reference. For example, consider a method that removes the first element of a list and adds it to the back of the list. With generic parameters, we can do the following:

static <T> boolean rotateOneElement(List<T> l){
    return l.add(l.remove(0));
}

with a wildcard, this is not possible since l.remove(0) would return capture-1-of-?, but l.add would require capture-2-of-?. I.e., the compiler is not able to deduce that the result of remove is the same type that add expects. This is contrary to the first example where the compiler can deduce that both is the same type T. This code would not compile:

static boolean rotateOneElement(List<?> l){
    return l.add(l.remove(0)); //ERROR!
}

So, what can you do if you want to have a rotateOneElement method with a wildcard, since it is easier to use than the generic solution? The answer is simple: Let the wildcard method call the generic one, then it works:

// Private implementation
private static <T> boolean rotateOneElementImpl(List<T> l){
    return l.add(l.remove(0));
}

//Public interface
static void rotateOneElement(List<?> l){
     rotateOneElementImpl(l);
}

The standard library uses this trick in a number of places. One of them is, IIRC, Collections.java

Comments