Bounded Type Parameters in Java

Naveen AnamaneniUpdated · 4 min read

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

Bounds.java▶ Try it
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));
    }
}
Output
274.0
5.75
Navika
42

How it works

  1. <T extends Number> accepts Integer, Double, Long and every other Number. Because every Number has doubleValue(), the method can call it on each T.
  2. <T extends Comparable<T>> accepts any type that can compare itself with its own kind, like String and Integer. So compareTo is allowed.
  3. "Navika" comes after "Advitha" alphabetically, so it's the max.

Passing a type outside the bound is a compile error:

Compiler output
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: Object

The Checkpoint: A Comparable Box

The bound limits which types fit, and gives access to their methods
ComparableBox.java▶ Try it
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());
    }
}
Output
Top mark: 95
Last name alphabetically: Srihitha

The 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:

DeclarationMeaning
<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

  1. Easy: Write static <T extends Number> double average(List<T> values) and test it with integers and doubles.
  2. Harder: Add a min() method to the checkpoint Box, and a sorted() method that returns a sorted copy of the items.

Quick check: Bounded types

4 questions
1What does <T extends Number> allow?
2Why can max call compareTo on T?
3Which keyword is used for an interface bound?
4In multiple bounds, what must come first?

Key Takeaways

Was this tutorial helpful?

Lesson 244 of 482