W3docs

Java Bounded Type Parameters

Constrain Java generic types with bounded type parameters using extends — single and multiple bounds.

A bounded type parameter is a type parameter with an extends clause that restricts what types can fill it in. Without a bound, T is treated as Object — the compiler has no way to know whether T has a compareTo or an intValue or any other method. With a bound, you make a promise to the compiler about what T is, and in exchange the compiler lets you call the methods that promise implies. Almost every interesting generic method has at least one bound somewhere in its signature.

The basic shape: <T extends Bound>

The syntax is extends between the parameter and its bound — same keyword whether the bound is a class or an interface:

public static <T extends Number> double sum(List<T> values) {
  double total = 0;
  for (T n : values) total += n.doubleValue();   // legal — T is-a Number
  return total;
}

Read <T extends Number> as "any T that is a Number or a subclass of one." Inside the body, you can now treat any T value as a Number — call doubleValue(), intValue(), anything in Number's public API.

Use it the same way as any other generic method:

sum(List.of(1, 2, 3));            // T = Integer — fine
sum(List.of(1.5, 2.5));           // T = Double  — fine
sum(List.of(1L, 2L, 3L));         // T = Long    — fine
sum(List.of("a", "b"));           // ❌ String is not a Number

Without the bound, sum couldn't even try to call doubleValue() — T would be erased to Object, and Object doesn't have that method. The bound is what makes the body legal.

extends works for interfaces too

In Java generics, extends is overloaded — it means "is a subtype of," whether the bound is a class or an interface. There's no separate implements keyword in a type parameter list:

public static <T extends Comparable<T>> T max(List<T> list) {
  T best = list.get(0);
  for (T candidate : list) {
    if (candidate.compareTo(best) > 0) best = candidate;
  }
  return best;
}

max(List.of(3, 1, 4, 1, 5, 9));            // T = Integer
max(List.of("Ada", "Grace", "Linus"));     // T = String

Comparable<T> is an interface, but the syntax is still extends. Read <T extends Comparable<T>> as "any T that is comparable to itself" — the constraint is what lets you call candidate.compareTo(best) inside the loop.

The little wrinkle here is the <T> inside the bound — it's the self-referential form we met in generic interfaces. It enforces that compareTo accepts a T, not just any Comparable. Without it, you could compare Integer to String and the type system wouldn't notice.

Multiple bounds

A type parameter can have more than one bound, joined by &:

public static <T extends Number & Comparable<T>> T maxNumber(List<T> list) {
  T best = list.get(0);
  for (T n : list) {
    if (n.compareTo(best) > 0) best = n;
  }
  return best;
}

Now T must be both a Number and a Comparable<T>. Inside the body you can call any method from either. Integer, Long, Double, BigDecimal all satisfy both — you can pass any of them.

A few rules:

  • The class bound (if any) must come first. <T extends Number & Comparable<T>> is legal; <T extends Comparable<T> & Number> is not.
  • At most one class bound. Java has single inheritance for classes, so this falls out of the language's existing rules.
  • Any number of interface bounds. Stack as many as you genuinely need; in practice, two is the usual maximum before the design needs a rethink.

The class-first rule reflects the bytecode: erasure replaces T with its leftmost bound, so the leftmost slot is special. We'll come back to that in Type Erasure.

Bounds on class-level type parameters

Bounds aren't unique to methods. A generic class or interface can bound its type parameters in the declaration:

public class SortedBag<T extends Comparable<T>> {
  private final List<T> items = new ArrayList<>();

  public void add(T item) {
    items.add(item);
    Collections.sort(items);
  }

  public T smallest() { return items.get(0); }
}

SortedBag<Integer> bag = new SortedBag<>();   // OK — Integer is Comparable<Integer>
// SortedBag<Object> bag2 = new SortedBag<>(); // ❌ Object is not Comparable<Object>

This is the right place for a bound that applies to the whole class. Every method, every field, every default operation has access to the constraint.

Lower bounds belong to wildcards, not type parameters

A common confusion: type parameters can have upper bounds (extends) but not lower bounds (super). This is legal:

public static <T extends Number> void foo(T x) { ... }

This is not:

public static <T super Integer> void bar(T x) { ... }   // ❌ no such syntax

Lower bounds do exist in Java's generics — but they only live on wildcards, the ? token, not on named type parameters. We'll cover the full ? extends / ? super story in the Wildcards chapter; for now, just know that super works on ? and not on T.

When to add a bound

The default should be "no bound" — the looser the constraint, the more types your method accepts. Add one when, and only when, the body needs to call a specific method on T:

  • "I need to compare T values" → <T extends Comparable<T>>.
  • "I need to add T values as numbers" → <T extends Number>.
  • "I need to read fields off T like a User" → <T extends User>.
  • "I need T to be auto-closeable so I can use it in try-with-resources" → <T extends AutoCloseable>.

If you're not calling a method on T, you don't need a bound — and adding one anyway just restricts your API for no reason.

A worked example: a bounded between and a sorted top

Two methods, each with a different style of bound. between uses Comparable<T> so it can clamp; topN uses Number & Comparable<T> so it can both compare and report a numeric sum.

java— editable, runs on the server

between accepts any Comparable — including Character, which is Comparable<Character> and has nothing to do with numbers. topN is narrower — it needs both ordering (to sort) and numeric values (to sum), so it stacks the two bounds with &. Each method asks for exactly the capability it uses, no more.

What's next

A bound on a type parameter pins down a single type at the call site — List<Integer> means "this list, this method, this type." Sometimes you want to be less specific: "a list of some Number — could be Integer, could be Double, I don't care." That's what wildcards are for, and they fix one of the biggest sources of confusion in Java's type system. Continue to Java Generic Wildcards.

Practice

Practice
You need a method that finds the largest element of a list. Which signature lets you call `compareTo` on the elements without restricting callers more than necessary?
You need a method that finds the largest element of a list. Which signature lets you call `compareTo` on the elements without restricting callers more than necessary?
Was this page helpful?