Bounded Type Parameters

A bounded type parameter restricts which types can be used as a type argument, letting generic code call methods guaranteed to exist on the bound.

By default, a type parameter like T is treated as if its only known type were Object, so generic code can't call any specialized method on a value of type T. A bounded type parameter — written <T extends SomeType> — narrows T to only types that are (or extend/implement) SomeType, both restricting what callers can pass and unlocking methods declared on the bound inside the generic code.

What Is a Bounded Type Parameter

Adding extends SomeType after a type parameter restricts the type argument to SomeType or one of its subtypes (for classes) or implementing types (for interfaces) — note that extends is used here for both class inheritance and interface implementation, unlike ordinary class declarations.

Without the bound, a.doubleValue() wouldn't compile because plain Object has no doubleValue() method; extends Number guarantees every possible T has one.

public static <T extends Number> double sumOf(T a, T b) {
    return a.doubleValue() + b.doubleValue(); // doubleValue() is declared on Number
}

double total = sumOf(3, 4.5);       // Integer and Double both extend Number...
// but actually: sumOf(3, 4.5) won't compile — both args must resolve to the SAME T
double total2 = sumOf(3, 4);        // OK: T inferred as Integer
double total3 = sumOf(3.5, 4.25);   // OK: T inferred as Double

Why Bounds Are Needed

An unbounded type parameter <T> is implicitly bounded by Object, so the compiler only lets you call methods that exist on every possible reference type — essentially just Object's methods (toString, equals, hashCode, etc.). A bound is how you tell the compiler "T will always have this specific capability," unlocking calls to that capability's methods.

public static <T> void printLength(T value) {
    // System.out.println(value.length()); // does not compile: Object has no length()
    System.out.println(value.toString());   // OK: every Object has toString()
}

Basic Syntax: <T extends Number>

The most common bound restricts a type parameter to a numeric type, letting generic numeric code call methods like intValue() or doubleValue() declared on the abstract class Number.

public class NumberBox<T extends Number> {
    private final T value;

    public NumberBox(T value) {
        this.value = value;
    }

    public double asDouble() {
        return value.doubleValue(); // legal because T is guaranteed to extend Number
    }
}

NumberBox<Integer> ib = new NumberBox<>(5);
NumberBox<Double> db = new NumberBox<>(3.14);
// NumberBox<String> sb = new NumberBox<>("x"); // compile-time error: String doesn't extend Number

Multiple Bounds

A type parameter can be bounded by more than one type at once, joined with &. At most one bound can be a class (and it must come first); the rest must be interfaces.

This bound requires T to be BOTH a Number (so numeric methods are available) AND Comparable to itself (so compareTo() is available) — useful when generic code needs both capabilities at once.

public static <T extends Number & Comparable<T>> T max(T a, T b) {
    return a.compareTo(b) >= 0 ? a : b;
}

Integer bigger = max(3, 7);       // Integer extends Number and implements Comparable<Integer>
Double biggerD = max(2.5, 1.1);   // Double extends Number and implements Comparable<Double>
Syntax rule for multiple bounds:
  <T extends ClassBound & Interface1 & Interface2 & ...>
  - at most one class bound, and it must be listed first
  - any number of interface bounds after it

Class + Interfaces as a Bound

Combining a class bound with one or more interface bounds is the typical shape of a multiple bound: the class supplies a base capability, and the interfaces add contract(s) the generic code also needs.

interface Taggable {
    String tag();
}

class Item implements Taggable {
    public String tag() { return "item"; }
}

public static <T extends Item & Comparable<T>> void process(T value) {
    System.out.println(value.tag() + " compared: " + value.compareTo(value));
}

Upper Bounds

extends on a type parameter always specifies an upper bound: the type argument must be the bound type or a subtype of it ("at most this general, going down the hierarchy is fine"). This is the only bound direction available for type parameter declarations — lower-bounded type parameters (super) exist only for wildcards, covered in the Wildcards topic.

class Animal { }
class Dog extends Animal { }
class Puppy extends Dog { }

public static <T extends Animal> void describe(T animal) {
    System.out.println(animal.getClass().getSimpleName());
}

describe(new Dog());    // OK: Dog is-an Animal
describe(new Puppy());  // OK: Puppy is-an Animal (transitively)
// describe("not an animal"); // compile-time error

Bounded Type Parameters on Classes

Bounds apply equally to a generic class's type parameter, restricting what types the class can be instantiated with, and enabling the class's methods to rely on the bound's API.

public class SortedPair<T extends Comparable<T>> {
    private final T first;
    private final T second;

    public SortedPair(T a, T b) {
        if (a.compareTo(b) <= 0) {
            first = a; second = b;
        } else {
            first = b; second = a;
        }
    }

    public T smaller() { return first; }
    public T larger()  { return second; }
}

SortedPair<Integer> pair = new SortedPair<>(7, 3);
System.out.println(pair.smaller() + " " + pair.larger()); // 3 7

Practical Use: Generic Numeric/Comparison Utilities

Bounded type parameters are how library methods like Collections.max, Collections.min, and Collections.sort work generically while still being able to compare elements.

// Simplified conceptual signature of java.util.Collections.max:
public static <T extends Comparable<T>> T max(List<T> list) {
    T best = list.get(0);
    for (T item : list) {
        if (item.compareTo(best) > 0) {
            best = item;
        }
    }
    return best;
}

Edge Cases

A bound restricts the type argument, but the generic method's implementation still can't assume anything beyond what the bound guarantees — you cannot, for example, call a Dog-specific method on a <T extends Animal> parameter unless the bound is Dog specifically. Also, the class bound (if any) must always be listed first when combining bounds.

class Animal { }
class Dog extends Animal { void bark() {} }

public static <T extends Animal> void greet(T a) {
    // a.bark(); // compile-time error: Animal has no bark(); bound only guarantees Animal's API
}

// public static <T extends Comparable<T> & Number> T bad(T a) { } // does not compile:
// class bound (Number) must come first
public static <T extends Number & Comparable<T>> T good(T a) { return a; } // correct order

Mental Model

Think of a bound as a promise the caller must keep in exchange for a capability the generic code needs. <T extends Number> says: "whatever type you plug in for T, it must be a Number (or subtype) — and in exchange, I'll be able to call doubleValue(), intValue(), and every other Number method on your value inside my implementation."

Quick Reference

<T extends Number>                         single class bound
<T extends Comparable<T>>                   single interface bound
<T extends Number & Comparable<T>>          class bound + interface bound (class first!)
<T extends Runnable & AutoCloseable>        multiple interface bounds

Rules:
  - "extends" is used for both class and interface bounds
  - at most one class bound, listed first
  - unbounded <T> is implicitly <T extends Object>
  - only upper bounds ("extends") allowed on type parameter declarations

Key Takeaways

A bounded type parameter, <T extends SomeType>, restricts the type argument to SomeType or its subtypes/implementers, and lets generic code call SomeType's methods on values of type T.

extends is used for both class and interface bounds on a type parameter — there is no separate implements keyword here.

Multiple bounds are joined with &; at most one can be a class, and it must be listed first.

An unbounded type parameter is implicitly bounded by Object, which is why only Object's methods are callable on it without a bound.

Type parameter declarations only support upper bounds (extends); lower bounds (super) apply only to wildcards, not type parameter declarations.

public static <T> double sumOf(T a, T b) { return a.doubleValue() + b.doubleValue(); // does not compile: T has no doubleValue() } public static <T extends Number> double sumOf(T a, T b) { return a.doubleValue() + b.doubleValue(); // OK: bound guarantees doubleValue() }

Without a bound, T is treated as Object, which has no doubleValue(); the bound extends Number is required before that method can be called on values of type T.

public static <T extends Comparable<T> & Number> T bad(T a) { return a; } // does not compile public static <T extends Number & Comparable<T>> T good(T a) { return a; } // class bound first

When combining a class bound with interface bounds, the class bound must be listed first; listing an interface before the class bound is a compile-time error.

class Animal { } class Dog extends Animal { void bark() {} } public static <T extends Animal> void greet(T a) { a.bark(); // does not compile: Animal (the bound) has no bark() } public static <T extends Dog> void greet(T a) { a.bark(); // OK: bound is Dog, which declares bark() }

A bounded type parameter only exposes the methods declared on its bound; assuming a more specific subtype's methods are available just because a specific object happens to be passed is a compile-time error — the bound itself must declare that method.

What does <T extends Number> mean, and what capability does it give generic code?

It restricts the type argument for T to Number or one of its subtypes, and it lets the generic code call any method declared on Number (like doubleValue() or intValue()) on values of type T.

Why is extends used for interface bounds too, e.g. <T extends Comparable<T>>, instead of implements?

In a type parameter bound, extends always means "is a subtype of, or implements" — Java reuses the single keyword extends for both class-extension and interface-implementation bounds on type parameters; there is no implements form here.

How do you write a type parameter bounded by both a class and an interface? What ordering rule applies?

Join them with &, e.g. <T extends Number & Comparable<T>>. If a class bound is present, it must be listed first; any interface bounds follow it.

What is the implicit bound of an unbounded type parameter <T>?

Object — an unbounded type parameter behaves as if it were declared <T extends Object>, so only methods declared on Object (toString, equals, hashCode, etc.) can be called on a value of type T without an explicit bound.

Can a type parameter declaration use a lower bound like <T super Number>?

No. Type parameter declarations only support upper bounds via extends. Lower bounds (super) are only valid on wildcards (e.g. List<? super Integer>), not on a type parameter's own declaration.

Interview: Why does <T extends Comparable<T>> T max(T a, T b) compile a.compareTo(b) but a bound of <T extends Object> would not?

Object has no compareTo() method, so without the Comparable<T> bound the call wouldn't compile; the bound guarantees every possible T supplies a compareTo(T) method, which the generic method body then relies on.

Interview: What real restriction does a bound place on callers, beyond what it grants the implementation?

A bound also limits which types can legally be used as the type argument at all — for example NumberBox<T extends Number> rejects NumberBox<String> at compile time, because String does not extend Number, even though the class never explicitly checks for that at runtime.

Write a generic method <T extends Number> boolean isPositive(T value) that returns true if the numeric value is greater than zero.

public static <T extends Number> boolean isPositive(T value) { return value.doubleValue() > 0; }

The bound extends Number allows calling doubleValue() on value, converting whatever numeric wrapper type T is (Integer, Double, Long, etc.) to a double for a uniform comparison against zero.

What does this print, and why does String work as the type argument here?

Prints banana. String implements Comparable<String>, satisfying the bound <T extends Comparable<T>>; "banana".compareTo("apple") is positive since banana lexicographically follows apple, so bigger() returns "banana".

Write a generic class Clamp<T extends Number & Comparable<T>> with a static method clamp(T value, T min, T max) that returns value restricted to the [min, max] range.

public class Clamp<T extends Number & Comparable<T>> { public static <T extends Number & Comparable<T>> T clamp(T value, T min, T max) { if (value.compareTo(min) < 0) return min; if (value.compareTo(max) > 0) return max; return value; } } // Usage: // int c = Clamp.clamp(15, 0, 10); // returns 10

The combined bound gives access to both Number's methods (unused here, but available) and Comparable's compareTo(), which is what clamp() actually needs to determine ordering against min and max.

What does this print, and what bound makes the compareTo() call legal?

Prints 7. Animal implements Comparable<Animal>, satisfying <T extends Comparable<T>>, so older()'s call to a.compareTo(b) compiles; since a2 (age 7) has the greater age, it's returned and result.age is 7.

public static <T extends Comparable<T>> T bigger(T a, T b) {
    return a.compareTo(b) >= 0 ? a : b;
}

String result = bigger("apple", "banana");
System.out.println(result);
class Animal implements Comparable<Animal> {
    int age;
    Animal(int age) { this.age = age; }
    public int compareTo(Animal other) { return Integer.compare(this.age, other.age); }
}

public static <T extends Comparable<T>> T older(T a, T b) {
    return a.compareTo(b) >= 0 ? a : b;
}

Animal a1 = new Animal(3);
Animal a2 = new Animal(7);
Animal result = older(a1, a2);
System.out.println(result.age);