Wildcards
Use the ? wildcard to write flexible generic methods that work with a range of parameterized types.
Overview
Key Points
- •List<?> accepts a list of any type; you can only read elements as Object
- •List<? extends T> accepts a list of T or any subtype; you can read as T but cannot add
- •List<? super T> accepts a list of T or any supertype; you can add T but reads return Object
- •Wildcards appear in parameter and field types, not as class type parameters
- •List<String> is not a subtype of List<Object>, but both are subtypes of List<?>
- •Unbounded wildcard is useful when you only call Object methods or Collection methods that do not involve the element type
- •Wildcards enable subtype polymorphism for generic types
Examples
Unbounded wildcard
import java.util.List;
public class Main {
// Accepts any List regardless of element type
static void printList(List<?> list) {
for (Object item : list) {
System.out.print(item + " ");
}
System.out.println();
}
static int countElements(List<?> list) {
return list.size(); // size() does not depend on element type
}
public static void main(String[] args) {
printList(List.of(1, 2, 3)); // 1 2 3
printList(List.of("a", "b", "c")); // a b c
printList(List.of(true, false, true)); // true false true
System.out.println(countElements(List.of(10, 20, 30))); // 3
}
}With List<?>, elements come out as Object. You cannot add anything (except null) because the real element type is unknown.
Upper-bounded wildcard for reading
import java.util.List;
public class Main {
static double average(List<? extends Number> nums) {
if (nums.isEmpty()) return 0;
double total = 0;
for (Number n : nums) total += n.doubleValue();
return total / nums.size();
}
static void copyToDouble(List<? extends Number> src, List<Double> dest) {
for (Number n : src) {
dest.add(n.doubleValue()); // safe: we read from src and write to dest
}
}
public static void main(String[] args) {
System.out.printf("%.2f%n", average(List.of(1, 2, 3, 4, 5))); // 3.00
System.out.printf("%.2f%n", average(List.of(1.5, 2.5, 3.0))); // 2.33
java.util.List<Double> out = new java.util.ArrayList<>();
copyToDouble(List.of(1, 2, 3), out);
System.out.println(out); // [1.0, 2.0, 3.0]
}
}List<? extends Number> is covariant for reading: you can treat elements as Number. You cannot add because the actual subtype is unknown.
Lower-bounded wildcard for writing
import java.util.List;
import java.util.ArrayList;
public class Main {
// Copies integers into any list that can hold Integer or a supertype
static void fill(List<? super Integer> dest, int from, int to) {
for (int i = from; i <= to; i++) {
dest.add(i); // safe: Integer is a subtype of everything in ? super Integer
}
}
public static void main(String[] args) {
List<Integer> intList = new ArrayList<>();
fill(intList, 1, 5);
System.out.println(intList); // [1, 2, 3, 4, 5]
List<Number> numList = new ArrayList<>();
fill(numList, 10, 13);
System.out.println(numList); // [10, 11, 12, 13]
List<Object> objList = new ArrayList<>();
fill(objList, 100, 102);
System.out.println(objList); // [100, 101, 102]
}
}List<? super Integer> accepts Integer, Number, and Object lists. When reading from it you only get Object back; the real element type is not known.
Common Mistakes
- Assuming List<String> is a subtype of List<Object>. It is not, due to type safety rules. Use List<?> or List<? extends Object> for a generic "any list" parameter
- Trying to add elements to List<? extends T>. The compiler rejects this because the specific subtype is unknown
- Using wildcards in class declarations: class MyClass<?> is not valid. Wildcards are for use sites, not class definitions
Note
When a method only reads from a collection, use <? extends T>. When it only writes to a collection, use <? super T>. When it does both or neither (just calls size()), use <?>.
Exercises
Write Java code and click Run to execute it instantly. When your output looks correct, mark as done.
Completed: 0 / 4 exercises
Knowledge Check
Question 1 of 4