Java Wildcard Extends: Upper Bounded Generics
java wildcard extends: Understand how `? extends T` works in Java generics, when to use it, its limitations, and how it differs from generic methods.
java wildcard extends requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
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 not a bug; it is the mechanism that preserves type safety at runtime.
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. Without a wildcard, you would need an overload for every numeric type or use raw types. With ? extends Number, one method covers 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 list is produced from; the destination is consumed into. 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 an upper bounded wildcard when a generic method is more appropriate. If you need to return a value of the same type as the input, a wildcard cannot express that relationship. For example, a method that returns the maximum element of a list needs to know the exact type to return it. A generic method captures the type 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 do not add runtime overhead. Java erases all type parameters and wildcards to their bounds at compile time. List<? extends Number> becomes List in bytecode, and the compiler inserts casts where needed. There is no extra allocation or indirection. The performance difference between using a wildcard and a concrete type is negligible.
The real cost is in compile-time checks. The compiler must ensure that every read from a ? extends collection is cast to the bound, which produces checked casts. These casts are safe and do not require runtime type checks beyond the standard array store checks. In practice, the JVM's type profile and escape analysis treat these casts as cheap.
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 the type parameter without a wildcard. They are often more flexible because 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 as:
public static <T> T first(List<? extends T> list) { return list.get(0); }
But you could also write it as public static <T> T first(List<T> list). The difference is that the wildcard version accepts a List<Integer> 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 of a specific supertype.
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 only consumes data, use ? extends. If it only produces data, use ? super. If it does both, use a concrete type or a generic method. This keeps the signature honest and prevents misuse at compile time. The java wildcard extends 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.