Why Are Generic Wildcards Needed?¶
In Java generics, we often encounter scenarios where we need to handle collections of a certain type, such as a method that needs to process List<Integer>, List<Double>, List<Number>, etc., without writing a separate method for each type. This is where generic wildcards come in handy.
Wildcards are represented by ? and come in two types: upper bounded wildcards (? extends T) and lower bounded wildcards (? super T). They help uniformly handle generic collections of different sub-types while ensuring type safety.
I. Upper Bounded Wildcards (? extends T)¶
What Are Upper Bounded Wildcards?¶
Upper bounded wildcards indicate that the elements in the collection are of type T or its subtypes.
Syntax: List<? extends T> or List<? extends Number> (assuming T is Number).
Example¶
Consider a class hierarchy where Object is the root class, Number extends Object, and Integer and Double extend Number. If we want a method to print all elements of collections of Number (and its sub-types), we can write:
// Print all elements in the list
public static void printNumbers(List<? extends Number> list) {
for (Number num : list) { // Elements are guaranteed to be Number or sub-types
System.out.println(num);
}
}
// Calling the method
List<Integer> intList = Arrays.asList(1, 2, 3);
List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);
List<Number> numberList = Arrays.asList(10, 20.5, 30);
printNumbers(intList); // Valid (Integer is a Number sub-type)
printNumbers(doubleList); // Valid (Double is a Number sub-type)
printNumbers(numberList); // Valid (Number is the upper bound)
Key Features of Upper Bounded Wildcards¶
-
Only Retrieval, No Addition
The compiler cannot know the specific sub-type of the collection (e.g.,List<Integer>orList<Double>). Adding elements could cause type mismatches.
❌ Invalid:list.add(new Integer(10));
✅ Valid: Only elements can be retrieved, and the return type isT(e.g.,Number). -
Type-Safe Element Retrieval
Elements are guaranteed to beTor its sub-types, so they can be safely received asT(e.g.,Number).
II. Lower Bounded Wildcards (? super T)¶
What Are Lower Bounded Wildcards?¶
Lower bounded wildcards indicate that the elements in the collection are of type T or its super-types.
Syntax: List<? super T> or List<? super Number> (assuming T is Number).
Example¶
To write a method that adds Number and its sub-types (e.g., Integer, Double) to a collection while handling collections with different super-types (e.g., List<Object>, List<Number>):
// Add Number and its sub-types to the list
public static void addNumbers(List<? super Number> list) {
list.add(new Integer(10)); // Valid (Integer is a Number sub-type)
list.add(new Double(20.5)); // Valid (Double is a Number sub-type)
list.add(new Number(30)); // Valid (Number is the lower bound)
}
// Calling the method
List<Object> objList = new ArrayList<>();
List<Number> numList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
addNumbers(objList); // Valid (Object is a Number super-type)
addNumbers(numList); // Valid (Number is its own super-type)
addNumbers(intList); // Valid (Integer is a Number sub-type)
Key Features of Lower Bounded Wildcards¶
-
Only Addition, No Specific Retrieval
Since the collection could be a super-type (e.g.,Object,Number), the element type is uncertain, so onlyObjectcan be retrieved.
❌ Invalid:Number num = list.get(0);
✅ Valid: OnlyObjecttype elements can be retrieved. -
Type-Safe Element Addition
Elements can be added asTor its sub-types, ensuring type compatibility.
III. Key Differences and Scenarios Summary¶
| Wildcard Type | Definition | Core Features | Use Case |
|---|---|---|---|
? extends T |
Elements are T or its sub-types | Can only retrieve elements (return T), cannot add elements (except null) | Read elements from collections of different sub-types |
? super T |
Elements are T or its super-types | Can only add elements (T or sub-types), cannot retrieve specific types (only Object) | Add elements to collections of different super-types |
IV. Pitfalls to Avoid¶
- Wildcards vs. Type Parameters
Wildcards are placeholders for “unknown types,” whileTis a type parameter (must specify the type). For example:
// Invalid: Upper bound wildcard cannot use T as a return type
public static <T extends Number> T getFirst(List<T> list) { ... }
// Valid: Use type parameter T explicitly
- Avoid Overusing Wildcards
If a method only needsList<Integer>, useList<Integer>directly for clarity. Wildcards are for scenarios with “unknown specific types.”
V. One-Sentence Summary¶
- Upper Bounded (
? extends T): Handles “all sub-types of T” and only allows retrieval. - Lower Bounded (
? super T): Handles “all super-types of T” and only allows addition of T/sub-types.
Mastering these rules allows flexible handling of generic collections with different sub-types/super-types in Java!