What are bounded type parameters in Java, and how do you use them to write a useful generic method?
A bounded type parameter restricts what a type argument may be. You write <T extends SomeType>, and the compiler then guarantees that every T is a SomeType — which means you can call SomeType's methods on a value of type T inside the method body.
That guarantee is the entire point. Without a bound, a generic method can only do things that work for any object, which usually means storing and retrieving it. With a bound, you can operate on it.
import java.util.List;
public class Main {
public static void main(String[] args) {
System.out.println("max of ints : " + max(3, 9, 4));
System.out.println("max of doubles: " + max(1.5, 0.5, 2.25));
System.out.println("sum of ints : " + sum(List.of(1, 2, 3)));
}
// T is known to be Comparable, so compareTo is available.
static <T extends Comparable<T>> T max(T first, T second, T third) {
T best = first;
if (second.compareTo(best) > 0) {
best = second;
}
if (third.compareTo(best) > 0) {
best = third;
}
return best;
}
// T is known to be a Number, so doubleValue is available.
static <T extends Number> double sum(List<? extends T> values) {
double total = 0;
for (T value : values) {
total += value.doubleValue();
}
return total;
}
}The output shows the bounds doing their job. The max method works for Integers and for Doubles because the bound says T is Comparable, and the compiler checks that every argument satisfies it. The sum method works with a List of Integers because the bound says the elements are Numbers.
Note the shape of the bound on max: extends Comparable<T> rather than Writing the raw Comparable would give you a compile error when you tried to call compareTo with a T argument, because the compiler could not prove the types line up.extends Comparable. The self-referential form is the correct one, and it is what lets the compiler compare two values of type T with each other.
Java supports only one bound per type parameter, and it must come first. If you need more than one constraint, the usual trick is to introduce an interface that extends the others and bound by that.