skip to content

Bridge Methods

When you override a generic supertype method, erasure leaves the signatures mismatched, so the compiler synthesizes a bridge method to keep dynamic dispatch working. A senior-level detail that shows you understand what the compiler emits.

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

questions

4

What is a bridge method in Java, and why does the compiler generate one when a generic supertype is overridden?

level: middleimportance: should knowfreq 35%

answer

  1. erasure makes super = compareTo(Object), sub = compareTo(MyType) — signatures differ
  2. compiler adds compareTo(Object) that casts + forwards
  3. restores polymorphic override after erasure
  4. flags: ACC_SYNTHETIC + ACC_BRIDGE; Method.isBridge()/isSynthetic()
  5. javap -p shows the extra method

basics

~20 s

A bridge method is an extra method the compiler adds automatically when you override a method from a generic class or interface. It exists so that overriding still works after generics are erased to raw types at compile time.

solid answer

~40 s

Java generics use type erasure: at compile time the type parameter is replaced by its bound (or Object), so a generic supertype's method ends up with an erased signature like compareTo(Object). When a subclass overrides it with a specific type, e.g. compareTo(MyType), the two signatures no longer match, so plain method overriding would break. To fix this the compiler emits a synthetic bridge method with the erased signature compareTo(Object) that simply casts its argument and forwards to the real compareTo(MyType). This restores correct polymorphic dispatch: a caller holding the supertype reference invokes the erased signature, the bridge runs, and the call lands on your real override. Bridge methods are marked synthetic and bridge in the bytecode and never appear in source; you only see them via reflection or a bytecode dump.

code

java · 18 lines
java
class MyDate implements Comparable<MyDate> {
    private final long millis;
    MyDate(long millis) { this.millis = millis; }

    // You write this:
    public int compareTo(MyDate other) {
        return Long.compare(this.millis, other.millis);
    }

    // The compiler generates (conceptually):
    // public int compareTo(Object other) {        // ACC_BRIDGE, ACC_SYNTHETIC
    //     return compareTo((MyDate) other);        // cast + forward
    // }
}

// Observe it:
// for (var m : MyDate.class.getDeclaredMethods())
//     System.out.println(m + " bridge=" + m.isBridge());

go deeper

for a junior

Knows generics get erased and that the compiler sometimes adds hidden methods, but may not name them precisely. Can state that overriding a generic method still works.

for a middle

Can explain that erasure makes the super signature use Object, the sub signature use the specific type, and the compiler inserts a forwarding bridge so the override still dispatches correctly.

for a senior

Articulates the exact mechanism (erased vs specific signature mismatch), the cast-and-forward body, the ACC_BRIDGE/ACC_SYNTHETIC flags, and reflection implications (isBridge filtering).

for a principal

Frames it in the larger erasure/backward-compatibility design tradeoff, contrasts with reified generics, and reasons about edge cases (covariant returns, multiple bridges, ClassCastException as the heap-pollution guard) and library/tooling impact.

## Background you need first **Generics** let you write a class or method parameterized by a type, e.g. `Comparable<T>`. **Type erasure** is the way Java implements generics: the JVM bytecode has no notion of `T`. At compile time, every type parameter is *erased* — replaced by its leftmost bound, or by `Object` if it has no bound. So `Comparable<T>` becomes, in bytecode, `Comparable` with a method `int compareTo(Object)`. This keeps generics backward-compatible with pre-2004 (pre-Java-5) bytecode and lets generic and non-generic code interoperate. **Overriding** means a subclass method with the *same signature* (name + parameter types) as a superclass/interface method replaces it for polymorphic dispatch. The JVM matches overrides by exact erased signature. ## The problem erasure creates Suppose you write: ```java class MyDate implements Comparable<MyDate> { public int compareTo(MyDate other) { ... } } ``` In source this *looks* like an override of `Comparable<MyDate>.compareTo(MyDate)`. But after erasure the interface only declares `int compareTo(Object)`. Your class declares `int compareTo(MyDate)`. These are **different signatures** — `compareTo(Object)` vs `compareTo(MyDate)` — so at the bytecode level your method does NOT override the interface method. If nothing were done, a call through a `Comparable` reference (which targets `compareTo(Object)`) would not reach your `compareTo(MyDate)`, breaking polymorphism. ## The fix: a synthetic bridge method The compiler closes the gap by generating an extra method in `MyDate`: ```java // generated by the compiler, not written by you public int compareTo(Object other) { return compareTo((MyDate) other); // cast + forward to the real method } ``` This is the **bridge method**. It has the *erased* signature `compareTo(Object)`, so it genuinely overrides the interface method, and its body casts the argument and delegates to your real, type-specific `compareTo(MyDate)`. Now a caller using the supertype invokes `compareTo(Object)`, lands on the bridge, which forwards to your code. Polymorphism is restored. ## How to recognize it The bridge method is flagged with two access flags in the class file: `ACC_SYNTHETIC` (compiler-generated, not in source) and `ACC_BRIDGE`. You can see it with `javap -p` (it shows the extra `compareTo(java.lang.Object)`), or via reflection: `Method.isBridge()` returns `true` and `Method.isSynthetic()` returns `true`. It never appears in your source code. ## Why "bridge" It *bridges* the erased supertype signature to your real specific-type signature, so two worlds — the type-erased view the JVM/old callers see, and the parameterized view you wrote — connect correctly. ## Consequences to remember - There can be *two* methods named `compareTo` in the bytecode of `MyDate`: yours and the bridge. This is legal in bytecode even though it would look like a duplicate in source. - Reflection that lists declared methods will return the bridge too — code that iterates methods should usually skip `m.isBridge()` ones. - The bridge does an unchecked cast `(MyDate) other`; if someone calls it with the wrong runtime type, a `ClassCastException` is thrown inside the bridge, which is exactly the heap-pollution protection erasure otherwise loses.

  • How can you observe a bridge method at runtime?
    Use reflection: iterate getDeclaredMethods() and check Method.isBridge() (and isSynthetic()), or inspect the bytecode with javap -p, which lists the extra erased-signature method flagged ACC_BRIDGE/ACC_SYNTHETIC.
  • What exception can a bridge method throw that the real method wouldn't?
    A ClassCastException, because the bridge performs an unchecked downcast from the erased parameter (e.g. Object) to the specific type before forwarding.

saying these in an interview costs you the question

  • Saying you write bridge methods yourself — they are compiler-generated only.
  • Claiming generics are reified in Java like in C# — Java uses erasure, which is the whole reason bridges exist.
  • Thinking the bridge is just a duplicate/overload with no purpose — it is the actual override the JVM dispatches to.
  • Saying erasure replaces T with Object always — it replaces with the leftmost bound, Object only when unbounded.

context

open as a page

What problems can bridge methods cause when you process methods via reflection, and how do you handle them?

level: seniorimportance: should knowfreq 28%

basics

~20 s

Reflection lists bridge methods alongside real ones, so you can see two methods with the same name. You should skip bridge methods (check Method.isBridge()) so you don't process duplicates or pick the one with the wrong, erased parameter/return types.

open as a page

How do bridge methods interact with covariant return types when overriding a method?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

Covariant returns let an override return a more specific type than the parent method. The JVM can't naturally dispatch on return type, so the compiler adds a bridge method with the parent's return type that calls your override and returns its result.

open as a page

Bridge methods are a symptom of how Java implemented generics. What is the underlying design tradeoff, and how would generics without bridge methods look?

level: principalimportance: nice to knowfreq 12%

basics

~20 s

Java added generics using type erasure to stay compatible with older code that had no generics, so type info is dropped at compile time. Bridge methods exist to patch up overriding after erasure. With reified generics (like C#), types are kept at runtime and no bridges are needed.

open as a page