So I was reading up on generics to re-familiarize myself with the concepts, especially where wildcards are concerned as I hardly ever use them or come across them. From the reading I've done I can not understand why they use wildcards. One of the examples I keep coming across is the following.
void printCollection( Collection<?> c ) {
for (Object o : c){
System.out.println(o);
}
}
Why would you not write this as:
<T> void printCollection( Collection<T> c ) {
for(T o : c) {
System.out.println(o);
}
}
Another example from the oracle website:
public static double sumOfList(List<? extends Number> list) {
double s = 0.0;
for (Number n : list)
s += n.doubleValue();
return s;
}
Why is this not written as
public static <T extends Number> double sumOfList(List<T> list) {
double s = 0.0;
for (Number n : list)
s += n.doubleValue();
return s;
}
Am I missing something?
The question mark (?) is known as the wildcard in generic programming. It represents an unknown type. The wildcard can be used in a variety of situations such as the type of a parameter, field, or local variable; sometimes as a return type.
Wildcards are nothing but the question mark(?) that you use in the Java Generics. We can use the Java Wildcard as a local variable, parameter, field or as a return type. But, when the generic class is instantiated or when a generic method is called, we can't use wildcards.
In generic code, the question mark (?), called the wildcard, represents an unknown type. The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; sometimes as a return type (though it is better programming practice to be more specific).
both bounded and unbounded wildcards provide a lot of flexibility on API design especially because Generics is not covariant and List<String> can not be used in place of List<Object>. Bounded wildcards allow you to write methods that can operate on Collection of Type as well as Collection of Type subclasses.
From Oracle:
One question that arises is: when should I use generic methods, and when should I use wildcard types? To understand the answer, let's examine a few methods from the Collection libraries.
interface Collection<E> {
public boolean containsAll(Collection<?> c);
public boolean addAll(Collection<? extends E> c);
}
We could have used generic methods here instead:
interface Collection<E> {
public <T> boolean containsAll(Collection<T> c);
public <T extends E> boolean addAll(Collection<T> c);
// Hey, type variables can have bounds too!
}
However, in both containsAll and addAll, the type parameter T is used only once. The return type doesn't depend on the type parameter, nor does any other argument to the method (in this case, there simply is only one argument). This tells us that the type argument is being used for polymorphism; its only effect is to allow a variety of actual argument types to be used at different invocation sites. If that is the case, one should use wildcards. Wildcards are designed to support flexible subtyping, which is what we're trying to express here.
So for the first example it's because the operation doesn't depend on the type.
For the second, it's because it only depends on the Number class.
Why make things more complicated than they need to be? The examples demonstrate the simplest thing that could possibly work – and these examples are not trying to illustrate generic methods.
Why would you not write this as:
<T> void printCollection( Collection<T> c ) { for(T o : c) { System.out.println(o); } }
Because System.out.println()
can accept objects, so nothing more-specific is needed.
Why is this not written as
public static <T extends Number> double sumOfList(List<T> list) { double s = 0.0; for (Number n : list) s += n.doubleValue(); return s; }
Again, because you don't need a different parameterization for every different T extends Number
. A non-generic method which accepts a List<? extends Number>
is sufficient.
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