Bounded Type Parameters in Java
Inside a plain <T>, Java knows nothing about T; it could be a String, a Thread, anything. So you can't add Ts together or compare them. A bound narrows the possibilities: <T extends Number> means "any type, as long as it's a Number". In exchange for accepting fewer types, you gain the right to call that type's methods, like doubleValue() or compareTo().
Calling Methods Through a Bound
import java.util.List;
public class Bounds {
static <T extends Number> double sum(List<T> numbers) {
double total = 0;
for (T n : numbers) {
total += n.doubleValue();
}
return total;
}
static <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) >= 0 ? a : b;
}
public static void main(String[] args) {
System.out.println(sum(List.of(91, 88, 95)));
System.out.println(sum(List.of(2.5, 3.25)));
System.out.println(max("Navika", "Advitha"));
System.out.println(max(17, 42));
}
}274.0
5.75
Navika
42How it works
<T extends Number>acceptsInteger,Double,Longand every otherNumber. Because everyNumberhasdoubleValue(), the method can call it on eachT.<T extends Comparable<T>>accepts any type that can compare itself with its own kind, likeStringandInteger. SocompareTois allowed."Navika"comes after"Advitha"alphabetically, so it's themax.
Passing a type outside the bound is a compile error:
Bound.java:3: error: method max in class Bound cannot be applied to given types;
static void use() { max(new Object(), new Object()); }
^
required: T,T
found: Object,Object
reason: inference variable T has incompatible bounds
upper bounds: Comparable<T>
lower bounds: ObjectThe Checkpoint: A Comparable Box
import java.util.ArrayList;
import java.util.List;
public class ComparableBox {
static class Box<T extends Comparable<T>> {
private final List<T> items = new ArrayList<>();
void add(T item) { items.add(item); }
T max() {
if (items.isEmpty()) {
throw new IllegalStateException("empty box");
}
T best = items.get(0);
for (T item : items) {
if (item.compareTo(best) > 0) {
best = item;
}
}
return best;
}
}
public static void main(String[] args) {
Box<Integer> marks = new Box<>();
marks.add(88);
marks.add(95);
marks.add(91);
System.out.println("Top mark: " + marks.max());
Box<String> names = new Box<>();
names.add("Srihitha");
names.add("Advitha");
System.out.println("Last name alphabetically: " + names.max());
}
}Top mark: 95
Last name alphabetically: SrihithaThe class-level bound Box<T extends Comparable<T>> lets max() call compareTo on any element. A Box<Object> wouldn't compile.
Multiple Bounds
A type parameter can have several bounds joined with &: one class (optional) plus any number of interfaces, with the class first:
| Declaration | Meaning |
|---|---|
<T extends Number & Comparable<T>> | A Number that is also comparable, like Integer |
<T extends Comparable<T> & Number> | Error: interface expected here, because the class must come first |
Common Mistakes
Writing implements in a bound. Type parameter bounds always use extends, even for interfaces.
Listing an interface before the class in multiple bounds. The class must come first.
Adding a bound you don't use. It only limits who can use your code.
Interview Corner
What are bounded type parameters?
A bounded type parameter restricts which types can be used as its argument, written <T extends Bound>. The bound can be a class or an interface, and multiple bounds can be combined with &, with at most one class, which must come first. Inside the generic code, methods of the bound can be called on values of type T, such as doubleValue() for T extends Number.
Practice
- Easy: Write
static <T extends Number> double average(List<T> values)and test it with integers and doubles. - Harder: Add a
min()method to the checkpointBox, and asorted()method that returns a sorted copy of the items.
Key Takeaways
Was this tutorial helpful?