skip to content

Upper Bounds

<T extends Number> restricts the argument and simultaneously grants access to the bound's members; with no bound, T erases to Object and you can call almost nothing. Interviewers ask why an unbounded T is so limited, and this is the answer.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

6

What is an upper bound on a generic type parameter in Java, and why would you use one?

level: juniorimportance: must knowfreq 70%

answer

  1. extends = type or subtype
  2. unlocks the bound's methods
  3. caller must pass a subtype
  4. default bound is Object
  5. extends covers interfaces too

basics

~20 s

An upper bound uses the extends keyword to say a type parameter must be a given type or a subtype of it, like <T extends Number>. This lets you call that type's methods inside the class or method.

solid answer

~40 s

An upper bound restricts a type parameter to a particular type or any of its subtypes using the extends keyword, e.g. <T extends Number>. Without a bound, the compiler only knows the parameter is some Object, so you can only call Object's methods on values of that type. By bounding it to Number, the compiler now guarantees every T is a Number, so you can safely call Number methods like intValue() or doubleValue() on a T. The bound is both a constraint (callers must supply Number or a subtype like Integer or Double) and a capability (your code gains access to the bound's members). You use it whenever your generic logic needs to do more than store and return values opaquely.

code

java · 11 lines
java
class Summer<T extends Number> {
    double sum(java.util.List<T> items) {
        double total = 0;
        for (T item : items) {
            total += item.doubleValue(); // legal: T is guaranteed to be a Number
        }
        return total;
    }
}

// Summer<Integer> ok; Summer<Double> ok; Summer<String> -> compile error

go deeper

for a junior

Knows extends restricts T to a type or its subtypes and lets you call that type's methods; can read <T extends Number>.

for a middle

Can explain the constraint-plus-capability duality and that the default bound is Object; writes a correct bounded generic method.

for a senior

Articulates how the bound is the compile-time contract that unlocks member access, distinguishes it from wildcards, and reasons about API ergonomics.

for a principal

Frames bounds within overall API design and variance strategy, weighing bounded parameters vs wildcards for library surface and evolution.

## Background: what generics are **Generics** let you write a class or method that works over many types while keeping type safety. You declare a **type parameter** (a placeholder like `T`, `E`, or `N`) in angle brackets. For example, `class Box<T>` means "a Box of some type T decided later." When someone writes `Box<String>`, the compiler substitutes `String` for `T`. ## The problem an upper bound solves With an **unbounded** parameter `<T>`, the compiler knows nothing about `T` except that it is *some* object. So inside the class the only methods you may call on a `T` value are those defined on `java.lang.Object` (`equals`, `hashCode`, `toString`, etc.). If you try `t.doubleValue()` the code won't compile, because not every possible `T` has that method. ## What an upper bound is An **upper bound** narrows the set of allowed types. You write `<T extends Bound>`, which says: *T must be `Bound` or any subtype (more specific type) of `Bound`.* The word **extends** here is reused from inheritance, but in generics it means "is `Bound` or a subtype of `Bound`" and it covers BOTH extending a class AND implementing an interface (you never write `implements` in a bound). Two consequences follow: 1. **Constraint on callers.** `new Calculator<Integer>()` is allowed because `Integer` is a subtype of `Number`; `new Calculator<String>()` is rejected at compile time because `String` is not a `Number`. 2. **Capability inside the code.** Because the compiler now *guarantees* every `T` is at least a `Number`, you may call any `Number` member on a `T` value: `t.intValue()`, `t.doubleValue()`, etc. The bound is the contract that unlocks those calls. ## A concrete example ```java class Summer<T extends Number> { double sum(java.util.List<T> items) { double total = 0; for (T item : items) { total += item.doubleValue(); // legal because T is a Number } return total; } } ``` Here `Summer<Integer>`, `Summer<Double>`, and `Summer<Long>` all work; `Summer<String>` does not compile. ## The default bound is Object When you omit a bound and just write `<T>`, the compiler treats it as if you had written `<T extends Object>`. So *every* type parameter has an upper bound; "unbounded" really means "bounded by Object." That is why an unbounded `T` only offers Object's methods. ## Why it matters Upper bounds are how generic code goes beyond opaque store-and-return containers to actually *operate* on the values it holds, while still being type-safe and reusable across a whole family of related types.

  • If you omit the bound and just write <T>, what bound does the compiler assume?
    It is implicitly <T extends Object>, which is why you can only call Object's methods on an unbounded T.
  • Why can't you call doubleValue() on an unbounded T?
    Because the compiler only knows T is some Object; doubleValue() is a Number method, and not every possible T is a Number, so the call is unsafe and rejected.

saying these in an interview costs you the question

  • Thinking extends in a bound means class-inheritance only — it also covers implementing interfaces
  • Saying an unbounded <T> has no bound — it is implicitly <T extends Object>
  • Believing the bound is only a restriction and forgetting it also grants access to the bound's members

context

open as a page

What is the difference between a bounded type parameter <T extends Number> and an upper-bounded wildcard <? extends Number>, and when would you choose each?

level: seniorimportance: must knowfreq 55%

basics

~20 s

A bounded type parameter <T extends Number> names a type you can reuse across the signature and return. A wildcard <? extends Number> is an anonymous unknown subtype used for a single parameter; you can read Numbers from it but generally cannot add to it.

open as a page

When a type parameter is declared without an explicit bound (just <T>), what is its bound, and what does that imply for the methods you can call?

level: juniorimportance: should knowfreq 45%

basics

~10 s

Writing <T> is the same as writing <T extends Object>. Because T is only known to be an Object, you can call only Object's methods on it, like equals, hashCode, and toString.

open as a page

How do you give a type parameter multiple upper bounds, and what rules govern the order and number of class versus interface bounds?

level: middleimportance: should knowfreq 40%

basics

~20 s

Combine bounds with the & symbol, like <T extends Number & Comparable<T>>. T must satisfy all of them. You can list at most one class, and it must come first; the rest must be interfaces.

open as a page

Why is a recursive (self-referential) upper bound like <T extends Comparable<T>> used, and what does it guarantee?

level: seniorimportance: should knowfreq 35%

basics

~20 s

A bound like <T extends Comparable<T>> says T must be comparable to its own type. It guarantees you can call a.compareTo(b) where both a and b are of type T, so you can safely order values of T.

open as a page

How does type erasure handle an upper-bounded type parameter, and what runtime artifacts (like casts and bridge methods) does it produce?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

At compile time Java erases the type parameter, replacing it with its upper bound (Object if none). The compiler then inserts casts where needed and may generate hidden bridge methods so overriding and polymorphism still work at runtime.

open as a page