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 ofList<Object>
. To write a generic printList method, useList<?>
: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)
(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.)
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 .
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)
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.
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
Technically, there is no difference between
<E> void printObjects(List<E> list) {
and
void printList(List<?> list) {
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With