Java Wildcard Extends: Upper Bounded Generics
Understand how `? extends T` works in Java generics: when to use the upper bounded wildcard, its write restrictions, and how it differs from generic methods.
An upper bounded wildcard, written ? extends T, is a core Java generics construct. It lets a method accept a collection of any subtype of T while preserving type safety. The examples below show the syntax, the read/write behavior, and the practical limits of the pattern.
When you write List<? extends Number>, you are declaring a list that holds some specific subtype of Number, but you don't know which one. This is the upper bounded wildcard, and it is the closest Java generics come to covariance. It lets you read Number values safely from the list, but it prevents you from adding elements to it. That asymmetry is intentional: it preserves type safety.
What Upper Bounded Wildcards Solve
Java arrays are covariant: a String[] is an Object[]. That allows code like Object[] arr = new String[1]; arr[0] = 42; to compile, only to fail at runtime with an ArrayStoreException. Generics were designed to avoid that runtime failure. List<String> is not a List<Object>, and you cannot assign one to the other. This invariance is safe but restrictive. A method that accepts List<Object> cannot accept List<String>, even though reading strings as objects is perfectly safe.
The upper bounded wildcard ? extends T relaxes that restriction for read operations. If a method parameter is List<? extends Number>, it can accept List<Integer>, List<Double>, or List<Number>. The method can read elements from the list and treat them as Number. It cannot add new elements because the actual element type is unknown. The compiler enforces this by rejecting any add call with an argument that is not null.
Declaring a Method That Accepts a Family of Types
Consider a utility that sums a collection of numbers. The upper bounded wildcard lets a single method cover all numeric subtypes:
public static double sum(List<? extends Number> numbers) { double total = 0.0; for (Number n : numbers) { total += n.doubleValue(); } return total; }
You can call this with List<Integer>, List<Double>, or List<BigDecimal>. The wildcard makes the method flexible without sacrificing type safety. Inside the loop, each element is treated as a Number, so doubleValue() is available. The method never needs to know the concrete type.
Reading vs Writing with ? extends T
The wildcard establishes a one-way relationship. You can read from a ? extends collection, but you cannot write to it. The compiler treats the element type as an unknown subtype of T. If you try to add an element, the compiler cannot verify that the element is of the unknown subtype, so it rejects the operation. The only value you can add is null, because null is assignable to any reference type.
List<? extends Number> nums = new ArrayList<Integer>(); nums.add(42); // Compile error: cannot add Integer to List<? extends Number> Number n = nums.get(0); // Works fine
This restriction is intentional. If you could add elements, you could accidentally put a Double into a List<Integer>, which would break type safety at runtime. The wildcard trades write capability for read flexibility.
The PECS Principle and When to Use ? extends
PECS stands for Producer Extends, Consumer Super. It is a mnemonic for choosing the right wildcard. If a method only reads elements from a collection, the collection is a producer, and you should use ? extends. If a method only writes elements to a collection, the collection is a consumer, and you should use ? super. If a method does both, use a concrete type or a generic method.
A typical example is copying from one list to another. The source produces elements; the destination consumes them. A correct signature uses ? extends for the source and ? super for the destination:
public static <T> void copy(List<? extends T> src, List<? super T> dest) { for (T item : src) { dest.add(item); } }
Without the wildcards, the method would only work when both lists have the exact same type. With ? extends on the source, you can copy from a List<Integer> into a List<Number>. The ? super on the destination allows the target list to be a supertype of T, so List<Object> also works.
Common Mistakes with Upper Bounded Wildcards
A frequent error is trying to write to a ? extends collection. Developers new to wildcards often write code like:
void addNumber(List<? extends Number> list, Number n) { list.add(n); // Compile error }
The compiler rejects this because list might be a List<Integer>, and adding a Number that is not an Integer would violate type safety. The fix is to change the parameter to List<Number> or List<? super Number> depending on the intended direction.
Another mistake is using only an upper bounded wildcard when the method also needs to capture a type relationship. If you need to return a value of the same type as the input, a bare wildcard cannot express that relationship. A generic method captures the type parameter, and you can combine it with a wildcard on the parameter:
public static <T extends Comparable<? super T>> T max(List<? extends T> list) { // implementation }
Here the wildcard on the parameter allows any list of a type that extends T, while the generic T is used in the return type. The wildcard alone cannot do that.
Performance and Type Erasure Considerations
Wildcards are erased at compile time to their bounds, so they add no runtime allocation or indirection. List<? extends Number> compiles to the same erased List type as other list types; the compiler inserts casts to Number when elements are read. In ordinary type-safe code, those casts are safe because the list was created with a subtype of Number. They are still runtime type checks, but the difference between using a wildcard and a concrete type is negligible in typical code.
A more significant concern is that wildcards can obscure the type relationship in complex signatures. A method with multiple wildcards, such as Map<? extends K, ? extends V>, is harder to read and can lead to subtle compile errors. If you find yourself fighting the compiler, consider whether a generic method would be clearer.
Alternatives to Wildcards: Generic Methods
Generic methods provide a way to capture a type parameter without a wildcard, and they allow the type to appear in multiple places, including the return type. For example, a method that returns the first element of a list can be written with a concrete List<T> parameter:
public static <T> T first(List<T> list) { return list.get(0); }
A wildcard version, public static <T> T first(List<? extends T> list), is more permissive: it accepts a List<Integer> even when T is inferred as Number, while the concrete version requires the list to be exactly List<T>. In many cases, the wildcard version is more permissive.
When you need to both read and write, a generic method is the only safe option. For example, swapping two elements in a list requires both read and write access. A wildcard would not allow the write. A generic method with List<T> works because the type is known inside the method.
Compatibility with Raw Types and Legacy Code
Before generics, collections were raw. Code that uses raw types interacts with wildcards in a predictable way. A raw List can be passed to a method expecting List<? extends Number>, but the compiler will issue an unchecked warning. Conversely, a List<? extends Number> can be passed to a raw List parameter, but you lose type safety. If you are modernizing legacy code, replacing raw types with wildcards is a gradual process. The wildcard ? is a good starting point because it accepts any type and prevents unsafe writes. The upper bounded wildcard is a refinement that allows reads as the declared upper bound.
A subtle edge case is the interaction with null. A List<? extends Number> can contain null elements, and you can add null to it. This is consistent with the rest of the type system. If you need to prohibit null, you must enforce that at the application level, not through generics.
Another edge case is the use of ? extends with interfaces. For example, List<? extends Runnable> accepts any list whose element type implements Runnable. This is useful when you only need to call run() on each element. The same read-only restriction applies.
When you design an API, choose the wildcard that expresses the actual contract. If a method reads from a collection, the collection is a producer, so use ? extends. If it writes to a collection, the collection is a consumer, so use ? super. If it does both, use a concrete type or a generic method. That keeps the signature honest and prevents misuse at compile time. The upper bounded wildcard pattern is a core tool for building flexible, type-safe libraries that work across a range of concrete types without sacrificing the guarantees that generics provide.