skip to content

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%

answer

  1. <T> equals <T extends Object>
  2. Object = root supertype
  3. only Object methods available
  4. tighten bound to gain methods
  5. no such thing as truly unbounded

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.

solid answer

~40 s

An omitted bound is not the absence of a bound: <T> is implicitly <T extends Object>. Object is the universal supertype of every reference type, so this is the loosest possible upper bound. The practical implication is that the compiler knows nothing about T beyond it being an Object, so the only members it will let you invoke on a T value are those declared on java.lang.Object: equals, hashCode, toString, getClass, and the wait/notify family. You cannot call domain-specific methods like compareTo or doubleValue because not every possible T has them. To gain those, you must tighten the bound, e.g. <T extends Comparable<T>> or <T extends Number>. This is why understanding the implicit Object bound clarifies exactly why bounded parameters are needed.

code

java · 10 lines
java
class Printer<T> {            // implicitly <T extends Object>
    void print(T value) {
        System.out.println(value.toString()); // ok: toString is on Object
        // value.length();                    // compile error: not on Object
    }
}

class Maxer<T extends Comparable<T>> {
    T max(T a, T b) { return a.compareTo(b) >= 0 ? a : b; } // bound grants compareTo
}

go deeper

for a junior

Knows <T> implicitly extends Object and so only Object's methods are callable on a T.

for a middle

Lists the specific Object methods available and shows how tightening the bound adds capabilities.

for a senior

Connects the Object default to API design choices about whether a generic should inspect or merely store its values.

for a principal

Uses the Object-default insight when designing minimal yet capable generic contracts and reasoning about erasure-driven dispatch.

## Setting the stage A **type parameter** is a placeholder for a type, written in angle brackets like `<T>`. The compiler erases generics at runtime but enforces them at compile time; to do that, it must know what operations are valid on a `T`. ## Every type parameter has an upper bound It is a common misconception that `<T>` means "T has no bound." In fact the Java Language Specification treats a missing bound as the bound **`Object`**. So these two declarations are identical: ```java class Box<T> {} class Box<T extends Object> {} ``` `Object` (`java.lang.Object`) is the root of the class hierarchy: every reference type ultimately extends it. Therefore bounding by `Object` allows *any* reference type to be substituted for `T` — the widest possible set. ## What you can do with an Object-bounded T Because the compiler only guarantees that a `T` value is an `Object`, the **only** instance methods it will permit on a `T` are the ones declared on `Object`: - `equals(Object)` - `hashCode()` - `toString()` - `getClass()` - `wait(...)`, `notify()`, `notifyAll()` (from Object's monitor methods) Attempting `t.compareTo(other)` or `t.length()` will not compile, because those members are not on `Object` and not every conceivable `T` would have them. ## Example of the limitation and the fix ```java class Printer<T> { void print(T value) { System.out.println(value.toString()); // ok: toString is on Object // System.out.println(value.length()); // NOT ok: length() is not on Object } } class Maxer<T extends Comparable<T>> { T max(T a, T b) { return a.compareTo(b) >= 0 ? a : b; // ok: bound grants compareTo } } ``` `Maxer` tightens the bound to `Comparable<T>`, so the compiler now guarantees a `compareTo` method exists. ## Why this matters Knowing that the default bound is `Object` explains the *whole point* of upper bounds: you tighten the ceiling from `Object` to something more capable so the compiler will let you call richer methods, while still accepting a family of subtypes. Unbounded generics are best for pure containers that only store and hand back values without inspecting them.

  • Name three methods you can legally call on an unbounded T.
    Any Object method: equals, hashCode, toString (also getClass and the wait/notify family).
  • How do you change the declaration so you can call compareTo on a T?
    Bound it to Comparable, e.g. <T extends Comparable<T>>, which guarantees a compareTo method exists.

saying these in an interview costs you the question

  • Saying <T> has no upper bound — it has Object
  • Assuming you can call compareTo or domain methods on an unbounded T
  • Confusing the implicit Object bound with the raw type (a separate concept)

context