Wildcards in Java
?, ? extends T (read), ? super T (write).
? means "some type I don't know"
**List<?> is a list of one specific type that's unknown here**, not a mix of anything. You can read elements as Object, but you can't add (except null). Wildcards appear in variable and parameter types, never after new.
List<?> xs = List.of("a", "b");
Object o = xs.get(0); // ✓ read
// xs.add("c"); // ✗
// new ArrayList<?>() // ✗ not allowed? extends T: safe to read
**List<? extends Number>** could be a List<Integer>, a List<Double>... Whatever it is, every element is a Number, so reading as Number is safe. That's why one method can accept both.
static double total(
List<? extends Number> xs) {
double t = 0;
for (Number n : xs) t += n.doubleValue();
return t;
}
total(List.of(1, 2)); // List<Integer> ✓
total(List.of(0.5)); // List<Double> ✓Your turn
The list really is an ArrayList<Double>. What happens?
List<? extends Number> xs =
new ArrayList<Double>();
xs.add(1.5);Runs fineCompile errorThrows ClassCastException
Show the answer
Compile error. The compiler only sees ? extends Number: the list *might* be a List<Integer>, so adding a Double can't be proven safe. **Only null can be added** to a ? extends list.
? super T: safe to write
**List<? super Integer>** could be a List<Integer>, List<Number> or List<Object>. Any of those can hold an Integer, so adding Integers is safe. Reading is the catch: the only type guaranteed for all three is **Object**.
static void fill(List<? super Integer> out) {
out.add(1); // ✓ always fits
out.add(2);
}Fill it up
What does this print?
static void fill(List<? super Integer> out) {
out.add(7);
}
void main() {
List<Object> objs = new ArrayList<>();
objs.add("hi");
fill(objs);
System.out.println(objs);
}[7][hi, 7]Compile error
Show the answer
A List<Object> is a valid List<? super Integer>, and adding an Integer to it is always safe. The existing "hi" stays: [hi, 7].
Reading from a ? super list
You put Integers in, so you expect Integers out? No: the list might be a List<Object> holding anything. **Reads from ? super Integer give you Object.**
List<? super Integer> out =
new ArrayList<Number>();
out.add(3);
Integer i = out.get(0); // ✗ compile error
Object o = out.get(0); // ✓Why API designers care
Wildcards make methods accept more callers without giving up safety. addAll(Collection<? extends E>) is why you can add a List<Integer> to a List<Number>. You'll read them in JDK signatures every day, even if you write them only now and then.
Key takeaways
- List<?>: a list of some unknown type — read as Object
- ? extends T: safe to read as T, can't add
- ? super T: safe to add T, reads give Object
- Wildcards appear in variable and parameter types, not in new
Java's wildcards were designed in the 2004 paper "Adding Wildcards to the Java Programming Language" by Mads Torgersen and colleagues. Torgersen later became the lead designer of C#.
Practice questions
What happens with this code?
List<? extends Number> nums = new ArrayList<Integer>();
nums.add(5);- Runs fine
- Compile error
- Throws ClassCastException
- Throws UnsupportedOperationException
Check your answer
Compile error. The list could really be a List<Double>, so the compiler can't prove adding an Integer is safe. Only null can be added to a ? extends list.
What does this print?
static double total(List<? extends Number> xs) {
double t = 0;
for (Number n : xs) { t += n.doubleValue(); }
return t;
}
void main() {
List<Integer> ints = List.of(1, 2);
List<Double> ds = List.of(0.5);
System.out.println(total(ints) + total(ds));
}- 3.5
- Compile error
- 3
- 12.5
Check your answer
3.5. List<? extends Number> accepts both a List<Integer> and a List<Double>, and every element can be read as a Number: 3.0 + 0.5 = 3.5.