When do Java generics require ? extends T instead of T and is there any downside of switching?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Understanding Java Generics and Bounded Wildcards
Java generics provide a way to create classes, interfaces, and methods with a placeholder for the types they operate with. The primary goal is to ensure type safety while still allowing code reusability. There are situations where you'd use <? extends T> instead of <T>, which mainly involves how you want to handle type flexibility and constraints.
The Concept of Bounded Wildcards
In Java generics, a wildcard (?) is a symbol that stands for an unknown type. The bound for a wildcard can either be upper or lower:
<? extends T>: This refers to an unknown type that is a subtype (or the same type) asT.<? super T>: This refers to an unknown type that is a supertype (or the same type) asT.
The commonly used upper-bounded wildcard <? extends T> is useful when you want to read from a generic object but not modify it, ensuring you work with at least type T or any of its subtypes.
When to Use <? extends T>
Reading from Collections
Imagine a method that outputs items from a list. If the list can contain items of type T or any subtype of T, the signature should use <? extends T>:
In this example, printListOfAnimals can take a list of Dog, Cat, or any subtype instances of Animal.
Producer-Extends Principle
This principle, often cited as "PECS" (Producer Extends Consumer Super), states that:
- Use
extendswhen you want to get values out of a structure and don’t want to insert. - Use
superwhen you want to insert values.
Downsides of Switching from <T> to <? extends T>
If you switch your method signature from <T> to <? extends T>, it comes with certain limitations:
- Insertion Restriction: You cannot add elements to a structure defined with
<? extends T>because Java cannot guarantee the type safety of insertions. For example:
In contrast, using <T>, you can insert elements of type T:
- Generic Flexibility: While
<? extends T>provides greater flexibility in terms of the types it can hold,<T>allows for more explicit control, especially when you need to both read and write to the collection.
Examples with Code
Case Study: Covariant Lists
Suppose you need to manage a list of numbers and calculate a sum:
This code will work with any list of elements that are a type of Number, such as Integer, Double, etc., but it cannot modify the list.
Conclusion
Choosing between <T> and <? extends T> heavily depends on the context of operations you plan to perform and the level of type flexibility required. <? extends T> is suitable for producing (reading), while <T> remains robust for scenarios that involve producing and consuming (reading and writing).
Summary Table
| Usage | <T> | <? extends T> |
| Type Flexibility | Less flexible, strict to type T | More flexible, accepts subtypes of T |
| Insertion | Supports adding elements | Does not support adding elements |
| Read/Write | Read and Write operations allowable | Read-only operations |
| Use Case | Both producing and consuming | Producing/Reading only |

