Bounded Type Parameters
Bounded Type Parameters
So far the type parameters we have seen — like <T> — accept any type. That flexibility is useful, but often you need to restrict which types are allowed, so that you can call specific methods on the value inside your generic code. That is exactly what bounded type parameters do.
The Problem Without Bounds
Imagine you want to write a method that finds the largest of two values. Your first attempt might look like this:
The compiler rejects this because T could be anything — a Thread, a custom Car class, anything. None of those are guaranteed to support compareTo. You need to tell the compiler that T must implement Comparable.
The extends Bound
You constrain a type parameter by writing <T extends SomeType>. This means T must be SomeType itself or a subtype of it — whether SomeType is a class or an interface. The keyword is always extends, even for interfaces.
Inside the method body, the compiler now knows every T has a compareTo method, so the call is safe.
extends covers both class inheritance and interface implementation. You would write <T extends Runnable> even though Runnable is an interface — there is no implements keyword in type parameter bounds.
A Generic Class with a Bound
Bounds work on class-level type parameters too. Here is a NumberBox that only accepts numeric types and can return the value as a double:
The compile error on the last line is exactly what we want — the bound acts as a guard at compile time, not at runtime.
Multiple Bounds
A type parameter can have more than one bound. The syntax uses & to separate them:
Two rules apply to multiple bounds:
- At most one class bound is allowed, and it must come first.
- All remaining bounds must be interfaces.
Here is a realistic example. Suppose you are writing a utility that needs objects that are both Serializable and Comparable — for example, to sort and then persist a list of items:
<T extends Serializable & AbstractBase> where AbstractBase is a class will not compile if AbstractBase appears after an interface. Always put the class first: <T extends AbstractBase & Serializable>.
Bounds in Recursive Generic Definitions
You may have noticed Comparable<T> used as the bound rather than the raw Comparable. This is called a recursive type bound and it is important. It says: T can only be compared to another T, not to some arbitrary type. This keeps comparisons type-safe:
You will see this pattern throughout the Java standard library — Collections.sort uses <T extends Comparable<? super T>> for the same reason.
When to Use Bounds
- Use a bound when your generic code needs to call a method that only certain types have (e.g.
compareTo,doubleValue). - Use multiple bounds when you need to combine capabilities (e.g. must be both sortable and serializable).
- Keep bounds as narrow as necessary. An overly tight bound restricts callers unnecessarily.
<T extends ArrayList>) makes your code brittle and often signals you should be using composition or a different design. Bounding on an interface (e.g. <T extends List>) is far more flexible.
Summary
Bounded type parameters allow you to restrict the set of types that can be substituted for a type variable. The extends keyword works for both class and interface bounds. Multiple bounds are joined with &, with the class bound (if any) listed first. Bounds unlock method calls that are impossible on unconstrained T, transforming generic code from merely flexible to genuinely useful.