Upper Bounded vs Lower Bounded Wildcard in Java: Key Differences
upper bounded wildcard (e.g., ? extends Type) restricts the unknown type to be a subtype of Type, allowing you to read from a generic structure safely. A lower bounded wildcard (e.g., ? super Type) restricts the unknown type to be a supertype of Type, enabling safe writes to a generic structure.Quick Comparison
Here is a quick side-by-side comparison of upper bounded and lower bounded wildcards in Java generics.
| Aspect | Upper Bounded Wildcard (? extends Type) | Lower Bounded Wildcard (? super Type) |
|---|---|---|
| Type Restriction | Unknown type must be a subtype of Type | Unknown type must be a supertype of Type |
| Usage | Safe to read items as Type or its subtype | Safe to write items of Type or its subtype |
| Read/Write | Allows reading, restricts writing (except null) | Allows writing, restricts reading (except Object) |
| Example | List<? extends Number> | List<? super Integer> |
| Common Use Case | Covariance - when you want to read from a generic | Contravariance - when you want to write to a generic |
Key Differences
An upper bounded wildcard uses the syntax ? extends Type and means the generic type can be Type or any subclass of it. This is useful when you want to read data from a generic collection safely as Type or its subtype, but you cannot add new elements (except null) because the exact subtype is unknown.
In contrast, a lower bounded wildcard uses ? super Type and means the generic type can be Type or any superclass of it. This allows you to add elements of Type or its subclasses safely, but reading from the collection only guarantees an Object type, since the exact supertype is unknown.
In summary, ? extends is for covariance (safe reading), and ? super is for contravariance (safe writing). This distinction helps maintain type safety while allowing flexibility in generic programming.
Code Comparison
This example shows how an upper bounded wildcard allows reading numbers safely but restricts adding new elements.
import java.util.List; import java.util.ArrayList; public class UpperBoundedExample { public static void printNumbers(List<? extends Number> list) { for (Number num : list) { System.out.println(num); } // list.add(10); // Compile error: cannot add to List<? extends Number> } public static void main(String[] args) { List<Integer> ints = new ArrayList<>(); ints.add(1); ints.add(2); printNumbers(ints); } }
Lower Bounded Wildcard Equivalent
This example shows how a lower bounded wildcard allows adding integers safely but restricts reading to Object type.
import java.util.List; import java.util.ArrayList; public class LowerBoundedExample { public static void addIntegers(List<? super Integer> list) { list.add(10); list.add(20); // Integer num = list.get(0); // Compile error: cannot guarantee Integer type on get Object obj = list.get(0); // Allowed, returns Object System.out.println(obj); } public static void main(String[] args) { List<Number> numbers = new ArrayList<>(); addIntegers(numbers); } }
When to Use Which
Choose ? extends Type (upper bounded wildcard) when you want to read data from a generic structure and do not need to add new elements. This is common when you want to process or display data safely.
Choose ? super Type (lower bounded wildcard) when you want to add elements to a generic structure but do not care about the exact type when reading. This is useful when you build or modify collections.
Remember: use upper bounded wildcards for covariance (read-only), and lower bounded wildcards for contravariance (write-only).
Key Takeaways
? extends Type to safely read from generics but avoid adding elements.? super Type to safely add elements to generics but reading returns Object.