Wildcards: ? extends (Upper Bounds)
Wildcards: ? extends (Upper Bounds)
You now know how to write generic classes and methods. But sometimes you do not need to produce a specific type — you only need to read from a generic collection and you want to accept many related types at once. Upper-bounded wildcards give you exactly that flexibility.
The Problem: Generic Types Are Invariant
One of the most surprising things in Java generics is that List<Double> is not a subtype of List<Number>, even though Double is a subtype of Number. This property is called invariance.
Why? If List<Double> were a List<Number>, you could write an Integer into it — breaking type safety. Java prevents this at compile time.
So how do you write a helper method that can sum any list of numbers — integers, doubles, floats?
Upper-Bounded Wildcards: ? extends T
The wildcard ? means "some unknown type". Adding extends T constrains that unknown type to be T or any subclass of T. The result — ? extends T — is called an upper-bounded wildcard.
Now you can call sum with a List<Integer>, a List<Double>, or even a List<BigDecimal>:
List<? extends Number> is covariant — it accepts List<Number>, List<Integer>, List<Double>, and any other List of a Number subtype. This mirrors the covariance of plain arrays (Integer[] is an Object[]), but at the generic level.
The Producer Role: Read, Do Not Write
There is a critical restriction: you cannot add elements to a List<? extends T>. The compiler does not know the exact type — it could be a List<Integer>, a List<Double>, or anything else. Writing an Integer into what might be a List<Double> would be unsound.
This is summarised by the Producer Extends half of the PECS mnemonic (Producer Extends, Consumer Super). A ? extends T list produces values of type T that you can read; it does not consume anything.
? extends T. If you only write to it, use ? super T (covered in the next lesson). If you do both, use a concrete type parameter.
Practical Example: Copying a List
The standard library method Collections.copy(dest, src) demonstrates PECS elegantly. Here is a simplified version:
Focus on src: it is List<? extends T> — we only read from it, so it is a producer. The dest is ? super T — we only write to it, so it is a consumer. You can now copy a List<Integer> into a List<Number>:
Upper Bounds Beyond Collections
Upper-bounded wildcards are not limited to List. They work with any generic type. Here is a method that compares two boxes where the contained type is Comparable:
List<? extends Number> and List<T extends Number> look similar but behave differently. The wildcard version is used at the call site — it accepts many specific types. The bounded type parameter T is used when you need to name the type and reuse it (e.g., return it). Choose the type parameter when the type identity matters; choose the wildcard when it does not.
Summary
- Generic types are invariant:
List<Double>is not aList<Number>. ? extends Tmakes a type covariant — it acceptsTand all subtypes.- You can read from a
? extends Tcollection but never write to it. - This is the Producer Extends part of PECS.
- It is ideal for helper methods that process a collection without modifying it.