What are bounded type parameters (extends/super) in generics?
Quick Answer
A bounded type parameter restricts which types can be substituted for a type variable. <T extends Number> means T must be Number or a subtype of it, letting the generic code call Number's methods (like doubleValue()) on values of type T. Multiple bounds are possible (T extends Number & Comparable<T>), but at most one can be a class, and it must come first.
Detailed Answer
Without a bound, a type parameter <T> can be anything, and the compiler only lets you treat values as Object — you can't call any type-specific method on them. A bounded type parameter restricts T to a subtype of a given type, unlocking that type's methods:
static <T extends Number> double sumAll(List<T> list) {
double sum = 0;
for (T t : list) sum += t.doubleValue(); // legal — Number guarantees doubleValue()
return sum;
}
sumAll(List.of(1, 2, 3)); // T = Integer, extends Number — OK
sumAll(List.of(1.0, 2.0)); // T = Double, extends Number — OK
sumAll(List.of("a", "b")); // compile error — String isn't a Number
Multiple bounds are allowed with &, useful for requiring both a base class and an interface — but only one class bound is allowed, and it must be listed first:
static <T extends Number & Comparable<T>> T max(List<T> list) {
T best = list.get(0);
for (T t : list) if (t.compareTo(best) > 0) best = t;
return best;
}
Despite the keyword extends being reused, it means "is a subtype of" in this context — it works for both class inheritance and interface implementation (<T extends Comparable<T>> is valid even though Comparable is an interface).