Java Upper Bounded Wildcard: Usage and Examples
java upper bounded wildcard: Learn how to use Java upper bounded wildcards (<? extends T>) to write flexible, type-safe generic code with practical examples.
When you write a method that should accept a collection of any subtype of a given type, the Java upper bounded wildcard (? extends T) is the tool that keeps the code type-safe without requiring a separate overload for every subtype. Consider a method that sums a list of numbers. Without a wildcard, you would need overloads for List<Integer>, List<Double>, and so on. With List<? extends Number>, a single method accepts all numeric lists and lets you read each element as a Number.
The Syntax of Upper Bounded Wildcards
An upper bounded wildcard is declared as ? extends BoundType. The question mark stands for an unknown type, and the extends clause restricts that unknown type to be either BoundType or a subtype of it. For example:
List<? extends Number> numbers;
This declaration means numbers can be a List<Integer>, List<Double>, List<Number>, or any list of a class that implements Number. The compiler treats the element type as Number when you read from the list, which is what makes the wildcard useful.
Reading from a Collection with an Upper Bounded Wildcard
The primary benefit of an upper bounded wildcard is that you can safely read elements from a structure without knowing the exact type. Consider a method that calculates the sum of any numeric list:
public static double sumList(List<? extends Number> numbers) { double sum = 0.0; for (Number n : numbers) { sum += n.doubleValue(); } return sum; }
You can call this method with List<Integer>, List<Double>, or List<BigDecimal>. Inside the method, each element is treated as a Number, so you can invoke doubleValue() without casting. This works because the wildcard guarantees that every element is at least a Number, even though the exact runtime type is unknown.
Why You Cannot Add Elements to an Upper Bounded Collection
While you can read from a List<? extends Number>, you cannot add elements to it, except null. The reason is that the exact element type is unknown. If you try to add an Integer to a list that might actually be a List<Double>, the compiler rejects it because that would break type safety. The only safe value is null, which is assignable to any reference type.
List<? extends Number> numbers = new ArrayList<Integer>(); // numbers.add(42); // Compile error: cannot add Integer to List<? extends Number> numbers.add(null); // Allowed, but rarely useful
This restriction is a direct consequence of the wildcard's purpose: it allows you to treat the collection as a producer of Number values, not a consumer. This leads to the well-known PECS principle (Producer Extends, Consumer Super), which guides when to use extends versus super wildcards.
Upper Bounded Wildcards vs. Generic Type Parameters
Sometimes you can choose between an upper bounded wildcard and a generic type parameter. For example, the sumList method could also be written as:
public static <T extends Number> double sumListGeneric(List<T> numbers) { double sum = 0.0; for (T n : numbers) { sum += n.doubleValue(); } return sum; }
Both versions accept the same set of arguments, but they differ in how they treat the type T. With a type parameter, you have a named type that can be used in multiple places within the method body, such as returning a T or accepting another List<T> parameter. With a wildcard, the type is anonymous and can only be used as the bound type when reading.
| Aspect | ? extends Number | <T extends Number> |
|---|---|---|
| Type name available | No | Yes |
| Can be used in return type | No | Yes |
| Can be used in multiple parameters | No | Yes |
| Best for | Single-use read-only access | Reusable type logic |
Use a wildcard when you only need to read from a structure and the exact type is irrelevant. Use a type parameter when you need to refer to the type elsewhere in the signature or body.
Common Pitfalls and Edge Cases
One common mistake is assuming that List<? extends Number> can accept a List<Object> or a List<String>. It cannot, because those types are not subtypes of Number. The bound is strict.
Another pitfall is trying to use a wildcard in a class declaration. You cannot write class Box<? extends Number>. Wildcards are only allowed in type arguments, not in type parameter declarations. For a class, you must use a named type parameter: class Box<T extends Number>.
Also, wildcards cannot have multiple bounds. A type parameter can be declared as <T extends Number & Comparable<T>>, but a wildcard cannot: ? extends Number & Comparable<?> is invalid. If you need multiple bounds, use a type parameter instead.
Performance and Type Erasure
Upper bounded wildcards have no runtime cost. Java generics are implemented via type erasure, meaning the compiler removes all generic type information and inserts casts where necessary. The wildcard itself disappears during compilation. There is no additional overhead for using ? extends Number versus a concrete type or a type parameter. The only difference is in compile-time type checking, which helps catch errors early without affecting runtime behavior.
This means you can use upper bounded wildcards freely in method signatures without worrying about performance penalties. The generated bytecode is essentially the same as if you had used a raw List with explicit casts, but the compiler guarantees the casts are safe.
When to Use an Upper Bounded Wildcard
Choose an upper bounded wildcard when your method or class only needs to read from a generic structure and the exact generic type is not important. Typical scenarios include:
- Methods that aggregate data from a collection, such as summing, averaging, or finding a maximum.
- Utility methods that operate on any subtype of a known interface, like
Collection<? extends Runnable>. - APIs that accept a collection of a specific subtype but do not need to modify it.
Avoid an upper bounded wildcard when you need to add elements to the structure or when the exact type must be known for a return value or a second parameter. In those cases, a generic type parameter gives you the flexibility and type safety you need.
For example, a method that copies elements from one list to another might need two type parameters to preserve the source and destination types independently. A wildcard would be insufficient because you cannot express the relationship between two unknown types.