Detailed explanation
A bounded type parameter is a generic type variable whose permitted types are restricted by an upper bound. In Java, you declare it using extends, whether the bound is a class or an interface: <T extends Number> or <T extends Comparable<T>>. An unbounded parameter, <T>, has Object as its implicit upper bound. Adding a bound establishes a compile-time contract: any type argument must satisfy that constraint, and the generic implementation can use the accessible methods guaranteed by the bound without casting.
Consider static <T extends Comparable<T>> T max(T a, T b) { return a.compareTo(b) > 0 ? a : b; }. The bound says that T must be comparable to values of type T. Therefore, the compiler accepts a.compareTo(b), which would not be valid for an unbounded T. In max(3, 5), Java boxes the int arguments into Integer objects and infers Integer as T; Integer implements Comparable<Integer>, so the call is valid and returns an Integer representing 5. A type that does not satisfy the bound cannot be used for this invocation. If the arguments compare as equal, this implementation returns b.
Bounds can also express multiple requirements, such as <T extends Number & Comparable<T>>. Here, T must extend Number and implement Comparable<T>. A type parameter may have at most one class bound, and that class must appear first; additional interface bounds are separated with &. These checks primarily happen at compile time. Under type erasure, T is erased to its leftmost bound, or Object when unbounded. The compiler preserves generic type safety through compile-time checking and inserts casts where necessary; it does not create a separate implementation for every type argument.
Interviewers usually look for both purposes of bounds: restricting valid type arguments and enabling useful operations inside generic code. They may also test the distinction between a bounded type parameter and a bounded wildcard. <T extends Number> declares a named type variable that can connect parameter and return types, while ? extends Number represents an unknown type within a type argument. For greater flexibility, the max method can use <T extends Comparable<? super T>>, allowing T to be compared through a Comparable implementation defined for a supertype.
Key points to remember
- An upper bound restricts type arguments and exposes the bound’s accessible methods inside generic code.
- Use extends for both class and interface bounds; an unbounded T implicitly extends Object.
- Comparable<T> guarantees that compareTo can accept a value of type T.
- Multiple bounds use &: at most one class is allowed, and it must appear first.
- Bounds are checked at compile time; erasure replaces a type variable with its leftmost bound.
Common mistakes candidates make
- Writing <T implements Comparable<T>> instead of using extends.
- Trying to declare <T super Integer>; lower bounds are supported for wildcards, not type parameter declarations.
- Treating <T extends Number> and ? extends Number as interchangeable, ignoring how a named T connects types across a method signature.
Likely follow-up questions
Why use Comparable<? super T> instead of Comparable<T>?
It accepts types that can be compared through a Comparable implementation targeting a supertype of T. For example, a subclass inheriting Comparable<Parent> can satisfy this more flexible bound even though it does not implement Comparable<Subclass>.
Can a bounded type parameter have a lower bound?
No, Java does not allow declarations such as <T super Integer>. Lower bounds are available through wildcards, such as List<? super Integer>, which can accept Integer values for insertion.
Does <T extends Number> let you add two T values with +?
No, Java generics do not provide numeric operator support for arbitrary Number subtypes. You can call methods such as doubleValue(), but converting and adding those results produces a double, not necessarily a T.
Interview tip: Define the restriction, walk through why compareTo compiles in the max example, and then mention multiple bounds and erasure.
