Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

java

generics

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)

like image 576
GhostCat Avatar asked Apr 04 '14 10:04

GhostCat


People also ask

What is the difference between generic and wildcard in Java?

(Since your edit) Those two function signatures have the same effect to outside code -- they both take any List as argument. A wildcard is equivalent to a type parameter that is used only once. Show activity on this post. ( ClassName is the name of the class containing the methods.)

What is generic type E?

List<E> is a generic type: a list that holds elements of some type represented by the placeholder E . This type has a method named add() , declared to take an argument of type E , and a method named get() , declared to return a value of type E .

What is E and T in Java generics?

T is meant to be a Type. E is meant to be an Element ( List<E> : a list of Elements) K is Key (in a Map<K,V> ) V is Value (as a return value or mapped value)

What is a generic type parameter in Java?

A type parameter, also known as a type variable, is an identifier that specifies a generic type name. The type parameters can be used to declare the return type and act as placeholders for the types of the arguments passed to the generic method, which are known as actual type arguments.


2 Answers

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

like image 106
gexicide Avatar answered Sep 21 '22 09:09

gexicide


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.

like image 35
Tanmay Patil Avatar answered Sep 24 '22 09:09

Tanmay Patil