52

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)

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • 2
    possible duplicate of [Type parameter vs unbounded wildcard](http://stackoverflow.com/questions/18142009/type-parameter-vs-unbounded-wildcard) – Duncan Jones Apr 04 '14 at 10:51

3 Answers3

50

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

Community
  • 1
  • 1
gexicide
  • 38,535
  • 21
  • 92
  • 152
  • @Duncan: Replaced it and clarified the middle part – gexicide Apr 04 '14 at 11:11
  • The *method itself* is more powerful, but what you do in the method itself and whether you call other private methods are internal implementation details. The *public API* of both are equally powerful. – newacct Apr 04 '14 at 22:47
  • @newacct: Right, concerning API, both are equally powerful. I was indeed referring to the implementation. – gexicide Apr 22 '14 at 10:53
17

Technically, there is no difference between

<E> void printObjects(List<E> list) {

and

void printList(List<?> list) {

  • When you are declaring a type parameter, and using it only once, it essentially becomes a wildcard parameter.
  • On the other hand, if you use it more than once, the difference becomes significant. e.g.

    <E> void printObjectsExceptOne(List<E> list, E object) {
    

    is completely different than

    void printObjects(List<?> list, Object object) {
    

    You might see that first case enforces both types to be same. While there is no restriction in second case.


As a result, if you are going to use a type parameter only once, it does not even make sense to name it. That is why java architects invented so called wildcard arguments (most probably).

Wildcard parameters avoid unnecessary code bloat and make code more readable. If you need two, you have to fall back to regular syntax for type parameters.

Hope this helps.

Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
Tanmay Patil
  • 6,882
  • 2
  • 25
  • 45
  • 1
    There are times when it is used only once but cannot be replaced by a wildcard. For example: 1) if the once it is used is in the return type, 2) if it is a recursively-bounded type parameter e.g. `>` – newacct Apr 05 '14 at 09:36
  • Wildcards are used only when passing a type as an argument. They are out of question when you are talking about type parameters. So first case obviously won't apply here. In second case it **is** used twice. – Tanmay Patil Apr 05 '14 at 09:40
  • You said "When you are declaring a type parameter, and using it only once, it essentially becomes a wildcard parameter." In the case of `public List newList();`, `E` is a type parameter that is only used once. – newacct Apr 05 '14 at 09:48
  • Yes I did. And `List> newList();` compiles as well. – Tanmay Patil Apr 05 '14 at 10:07
  • Right, but the two mean very different things. So the type parameter cannot be turned into a wildcard. – newacct Apr 05 '14 at 10:10
  • E is a type argument in this case, not a type parameter. Only type arguments can be converted to wildcards. Sorry if I have written it otherwise. Will edit it if something's wrong after checking. – Tanmay Patil Apr 05 '14 at 10:14
9

Both solutions are effectively the same, it's just that in the second one you are naming the wildcard. This can come handy when you want to use the wildcard several times in the signature, but want to make sure that both refer to the same type:

static <E> void printObjects(List<E> list, PrintFormat<E> format) {
Djizeus
  • 4,161
  • 1
  • 24
  • 42