1. Overview
In this quick tutorial, we’ll see the similarities and differences between <?> and <? extends Object> in Java Generics.
However, this being an advanced topic, it’s imperative to get a basic understanding of the subject before we dive into the crux of the matter.
2. Background of Generics
Generics were introduced in JDK 5 to eliminate run-time errors and strengthen type safety. This extra type-safety eliminates casting in some use cases and empowers programmers to write generic algorithms, both of which can lead to more readable code.
For example, pre-JDK 5, we’d have to work with the elements of a list using casting. This, in turn, created a certain class of runtime errors:
List aList = new ArrayList();
aList.add(new Integer(1));
aList.add("a_string");
for (int i = 0; i < aList.size(); i++) {
Integer x = (Integer) aList.get(i);
}
Now, this code has two issues we’d like to address:
- We need an explicit cast to extract values from aList – the type depends on the variable type on the left – Integer in this case
- We’ll get a runtime error on the second iteration when we’re trying to cast a_string to an Integer
Generics fill the role for us:
List<Integer> iList = new ArrayList<>();
iList.add(1);
iList.add("a_string"); // compile time error
for (int i = 0; i < iList.size(); i++) {
int x = iList.get(i);
}
The compiler will tell us that it’s not possible to add a_string to a List of type Integer, which is better than finding out at runtime.
Moreover, no explicit casting is needed since the compiler already knows that iList holds Integers. Additionally, due to the magic of unboxing, we didn’t even need an Integer type, its primitive form is enough.
3. Wildcards in Generics
A question mark, or wildcard, is used in generics to represent an unknown type. It can have three forms:
- Unbounded Wildcards: List<?> represents a list of unknown type
- Upper Bounded Wildcards: List<? extends Number> represents a list of Number or its sub-types such as Integer and Double
- Lower Bounded Wildcards: List<? super Integer> represents a list of Integer or its super-types Number and Object
Now, since Object is the inherent super-type of all types in Java, we would be tempted to think that it can also represent an unknown type. In other words, List<?> and List