Bounded type parameters in Java
<T extends Number>, multiple bounds with &.
sum method for Integers, Doubles AND Longs, but x.doubleValue() won't compile on a plain T. How do you tell Java "T is some kind of Number"?A plain T knows nothing
Inside <T>, the compiler only knows T is some Object. So only Object's methods are available: no doubleValue(), no compareTo().
static <T> double sum(List<T> xs) {
double t = 0;
for (T x : xs) t += x.doubleValue(); // ✗
return t;
}Add a bound
**<T extends Number> means T must be Number or a subclass. That restriction unlocks the bound's methods** on T: every T is guaranteed to have doubleValue().
static <T extends Number> double sum(
List<T> xs) {
double t = 0;
for (T x : xs) t += x.doubleValue(); // ✓
return t;
}Your turn
What does this print?
static <T extends Number> double sum(
List<T> xs) {
double t = 0;
for (T x : xs) t += x.doubleValue();
return t;
}
void main() {
IO.println(sum(List.of(1, 0.5, 2L)));
}33.5Compile error
Show the answer
Integer, Double and Long are all Numbers, so the call compiles. Each becomes a double: 1.0 + 0.5 + 2.0 = 3.5.
Strings at the door
What happens?
static <T extends Number> double sum(
List<T> xs) {
return 0;
}
void main() {
System.out.println(sum(List.of("1", "2")));
}Prints 0.0Compile errorThrows ClassCastException
Show the answer
String is not a subtype of Number, so it doesn't satisfy the bound. The call is rejected at compile time, before any code runs.
extends means "is a subtype of"
In a bound, **extends covers both classes and interfaces**. There's no implements in type parameters: a bound only cares that T is a subtype, not how it got there.
<T extends Number> // a class
<T extends Runnable> // an interface
<T extends Comparable<T>> // also fineMultiple bounds: class first
Join several bounds with **&. If one of them is a class, it must come first**. A comma would declare a second, separate type parameter, and | isn't allowed at all.
<T extends Number & Comparable<T>> // ✓
<T extends Comparable<T> & Number> // ✗
<T extends Number, Comparable<T>> // ✗Where you'll see bounds
Statistics helpers take <T extends Number>, sorting and max/min utilities take <T extends Comparable<...>>, and EnumMap<K extends Enum<K>, V> only accepts enum keys. Bounds let a library say exactly what it needs from your types, and let the compiler enforce it.
Key takeaways
- <T extends Number>: T must be Number or a subclass
- Bounds unlock the bound's methods on T
- extends is used for interfaces too: <T extends Comparable<T>>
- Multiple bounds: <T extends Number & Comparable<T>>, class first
A type parameter can have any number of interface bounds, but at most one class bound, and the class must come first. It's the same rule as a class that extends one class but implements many interfaces.
Practice questions
What does this print?
static <T extends Number> double sum(List<T> xs) {
double total = 0;
for (T x : xs) {
total += x.doubleValue();
}
return total;
}
void main() {
System.out.println(sum(List.of(1, 2.5, 3L)));
}- 6
- 6.5
- Compile error
- Throws ClassCastException
Check your answer
6.5. Integer, Double and Long are all Numbers, so the call compiles. Each is converted with doubleValue(): 1.0 + 2.5 + 3.0 = 6.5.
What does this print?
static <T extends Number> double sum(List<T> xs) {
return 0;
}
void main() {
System.out.println(sum(List.of("a", "b")));
}- 0.0
- Compile error
- Throws ClassCastException
- 0
Check your answer
Compile error. String is not a subtype of Number, so it doesn't satisfy the bound and the call is rejected at compile time.