Bounded types and generic methods
Bounded type parameters, multiple and recursive bounds, generic methods, inference, self-type builders and realistic bounded generic APIs.
On this page
- Picture it first: the bound is a gate
- Bounded types under the hood: upper and multiple bounds
- Recursive bounds: types that talk about themselves
- Advanced: self-type builders and generic methods
- Type inference, target typing and var
- Bounded wildcards or bounded type parameters?
- How to say it in the interview
🧱 What are bounded types and generic methods?
A bounded type parameter is a generic type with a constraint, like <T extends Number>, so the compiler lets you use Number operations on T. A generic method declares its own type parameter, like <T> T first(List<T> list), even when the class is not generic. Think of a bound as an entrance rule for a meeting room: anyone can enter the generic room, but the bounded room checks that every guest has the badge you need.
For this lesson, we'll focus on upper bounds, multiple bounds, recursive bounds, the self-type builder pattern, generic methods versus generic classes, static generic factories, type inference, var, rare generic constructors, bounded wildcards versus bounded type parameters, and the inference errors you see in real code. We will link to Generics basics for the syntax foundation and How type erasure works for the runtime story.
Below the line (out of scope)
- Wildcard variance and PECS in detail: Wildcards and PECS
- Raw types, heap pollution and erased runtime types: How type erasure works
- Natural ordering and comparator design: Comparable vs Comparator
The 30-second answer
A bounded type parameter says, "the caller chooses T, but T must satisfy this constraint." <T extends Number> lets the method call doubleValue; <T extends Number & Comparable<T>> requires both a class and an interface, with the class first, and erases to the first bound. Recursive bounds such as <T extends Comparable<T>> let a type say it can compare itself with its own kind, and Enum<E extends Enum<E>> uses the same idea. Use a generic class when the whole object stores one type relationship, a generic method when only one operation needs it, and a static generic factory when construction should infer the type. If a type parameter appears only once in a public parameter, a bounded wildcard is often clearer; if it connects two arguments or the return type, name it.
What you’ll learn
- Use upper bounds like
<T extends Number>to call bound methods safely - Explain multiple bounds syntax, class-first ordering and erasure to the first bound
- Recognize recursive bounds such as
<T extends Comparable<T>>andEnum<E extends Enum<E>> - Apply the self-type builder pattern with
Builder<T extends Builder<T>>andself() - Choose between generic classes, generic methods, static factories and rare generic constructors
- Explain Java 8+ target typing,
varwith generics and inference failure messages - Choose bounded wildcards or bounded type parameters using the rule: if the type appears once, prefer a wildcard
Interview radar
Asked often: Why does <T extends Number> use extends for classes and interfaces?
Core follow-up: Explain <T extends Number & Comparable<T>>, including why Number must come first.
Production angle: A builder hierarchy returns the base builder from fluent methods, so subclass-specific methods disappear unless you use a self-type.
01Picture it first: the bound is a gate
A bound is the compiler's gate in front of a generic method. For <T extends Number & Comparable<T>>, the type must be a Number, and it must know how to compare itself with the same type. Integer, Double and BigDecimal pass; String and AtomicInteger fail for different reasons.
Bound checks for T extends Number and Comparable of T showing Integer, Double and BigDecimal accepted while String and AtomicInteger are rejected
Notice the two independent checks. String is comparable but not a number. AtomicInteger extends Number, but it does not implement Comparable<AtomicInteger>, so it still fails.
02Bounded types under the hood: upper and multiple bounds
The word extends in a type parameter means "is a subtype of", not only "extends a class". That is why Java uses the same keyword for classes and interfaces in generic bounds: <T extends Serializable> is legal even though Serializable is an interface.
Try it yourself first
Before reading on, decide why the method below can call doubleValue() without casts, and why it can accept both Integer and BigDecimal.
import java.math.BigDecimal;
import java.util.List;
static <T extends Number> double average(List<T> values) {
if (values.isEmpty()) throw new IllegalArgumentException("empty list");
double total = 0.0;
for (T value : values) {
total += value.doubleValue();
}
return total / values.size();
}
System.out.println(average(List.of(10, 20, 30))); // 20.0
System.out.println(average(List.of(new BigDecimal("1.5")))); // 1.5
The compiler treats every T as at least a Number, so doubleValue() is available. It still remembers the exact type at compile time for type checking, even though erasure will remove most of that detail later.
Multiple bounds add more promises:
static <T extends Number & Comparable<T>> T larger(T left, T right) {
return left.compareTo(right) >= 0 ? left : right;
}
Integer winner = larger(10, 25);
Double rate = larger(0.18, 0.20);
There are two syntax rules worth saying out loud:
- If there is a class bound, it must come first:
<T extends Number & Comparable<T>>, not<T extends Comparable<T> & Number>. - Erasure uses the first bound. Here
Terases toNumber; if the first bound were an interface, it would erase to that interface.
That first-bound erasure matters for binary compatibility and for the casts the compiler inserts. We cover erasure properly in How type erasure works, but the interview version is: put the class first because the language requires it, and remember erasure follows the first bound.
Premium lesson
Keep reading with the Java Pass
The rest of this lesson is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.
- Recursive bounds: types that talk about themselves
- Advanced: self-type builders and generic methods
- Type inference, target typing and var
- Bounded wildcards or bounded type parameters?
- How to say it in the interview
From ₹199 · one-time payment · No auto-renewal