How does the compiler erase a bounded type parameter, and why is the order of bounds significant?
answer
- Bound replaces Object as the erasure target
- Multiple bounds → leftmost wins
- Class bound must come first (at most one class)
- Order affects erased signature + bridge methods
- Put the richest/class bound first to reduce casts
basics
~20 sA type parameter with a bound, like T extends Number, is replaced by that bound (Number) instead of Object. With several bounds (T extends A & B), the compiler uses the first one listed, so the order you write the bounds matters.
solid answer
~50 sWhen a type parameter is unbounded it erases to Object, but when it has an upper bound the compiler erases it to that bound — `T extends Number` makes `T` become `Number` in the erased signatures and fields. This is useful because the erased code can then call `Number`'s methods directly. With multiple bounds, `T extends Number & Comparable<T>`, erasure picks the **leftmost** bound (`Number`). The order matters because (a) the erased type is the leftmost bound, which affects the bytecode-level type and bridge-method generation, and (b) Java requires that if one bound is a class it must be listed first (you can have at most one class bound, and any number of interface bounds after it). So you place the class first both because the language forces it and because it determines the erasure. Choosing the most useful bound first can reduce inserted casts.
go deeper
Recognizes that T extends Number means T behaves like a Number, without needing the erasure details.
States that a bounded parameter erases to its bound, multiple bounds erase to the leftmost, and the class bound must be first.
Connects bound ordering to inserted casts, erased signatures, and bridge-method generation, and gives the practical 'richest bound first' guidance.
Reasons about how intersection-type erasure interacts with overriding/bridge methods and API/binary compatibility when bounds change.
## Background: what a bound is A **type parameter** like `T` can be constrained by an **upper bound** using `extends`: `class Box<T extends Number>` says "T must be Number or a subtype." The bound lets the compiler allow operations valid on that bound — inside `Box` you may call `value.intValue()` because `T` is known to be a `Number`. ## Erasure of a single bound Recall the core rule: **an unbounded parameter erases to `Object`; a bounded parameter erases to its bound.** So: ``` class Box<T> { T v; } // erased: Object v; class Box<T extends Number> { T v; } // erased: Number v; ``` This is not just cosmetic. Because the erased field/return type is `Number`, the synthetic casts the compiler inserts at call sites cast to `Number` (or the more specific declared type), and code *inside* the class can invoke `Number` methods without any cast at all. ## Multiple bounds and why order matters A parameter may have several bounds joined by `&`: `T extends Number & Comparable<T>`. This is an **intersection** of bounds — T must satisfy all of them. Two rules govern this: 1. **Language rule:** at most **one** of the bounds may be a **class**; the rest must be **interfaces**; and if a class bound is present it must be **first**. `T extends Comparable<T> & Number` is a compile error — the class must lead. 2. **Erasure rule:** the parameter erases to the **leftmost** bound. So for `T extends Number & Comparable<T>`, T erases to `Number`. If all bounds are interfaces, e.g. `T extends Comparable<T> & Serializable`, it erases to the leftmost interface, `Comparable`. Because erasure takes the leftmost bound, **the order is significant**: it determines the runtime/bytecode type of the parameter, which in turn drives: - The type of inserted casts and the erased method signatures. - **Bridge methods**: when a generic class overrides/implements methods, the compiler may emit synthetic *bridge methods* to reconcile the erased signature with the overridden one; the chosen leftmost bound influences these. ## Practical guidance List the bound that gives you the richest, most-used API first (and it must be the class if you have one). This minimizes casts and keeps the erased type maximally useful. The interface bounds after it still constrain the source-level type checking, they just don't change the erasure. ## Quick sanity check ``` static <T extends Number & Comparable<T>> T max(T a, T b) { return a.compareTo(b) >= 0 ? a : b; // compareTo from Comparable, allowed } // Erased signature: Number max(Number a, Number b) ``` The method returns `Number` after erasure; callers get a synthetic cast back to the actual `T`.
- What is the erased return type of <T extends Comparable<T> & Serializable> T first(List<T> xs)?Comparable — it is the leftmost bound (both are interfaces here, so the first one listed is used).
- Why must the class bound be listed first?A type can extend only one class but many interfaces; requiring the single class to lead makes the erasure (leftmost bound) deterministic and matches Java's single-inheritance model.
saying these in an interview costs you the question
- Claiming a bounded parameter still erases to Object
- Saying you can list multiple class bounds
- Putting an interface before a class bound (compile error)
- Thinking the order of interface bounds never matters — it changes the erasure if no class bound is present