Understanding Java Bridge Methods: Why the Compiler Generates Synthetic Bridge Methods
This article explains how Java's compiler creates synthetic bridge methods during type erasure for generic interfaces and overridden methods, demonstrates how to identify them via reflection, and explores various scenarios where bridge methods appear, providing code examples and bytecode analysis.
The author investigates a practical problem where, after implementing a generic interface, a class contains two methods with the same name process —one with a String parameter and another synthetic one with an Object parameter—making it unclear which method to invoke via reflection.
Using a simple Operator<T> interface and a concrete UserInfoOperator implementation, the article shows the source code:
/**
* @author renzhiqiang
* @date 2022/2/20 18:30
*/
public interface Operator<T> {
void process(T t);
}
/**
* UserInfoOperator
* @author renzhiqiang
* @date 2022/2/20 18:30
*/
public class UserInfoOperator implements Operator<String> {
@Override
public void process(String s) {
// do something
}
}When inspecting the compiled .class files with the jclasslib plugin, two process methods are visible in UserInfoOperator : one with a String argument and another synthetic bridge method with an Object argument, marked as public synthetic bridge .
The bridge method is generated because, after type erasure, the generic interface method becomes void process(Object) . Without the bridge, the class would not correctly override the interface method, causing a compilation error. The compiler therefore creates a bridge method that simply casts the Object to String and delegates to the real implementation:
// automatically generated bridge method
public void process(Object object) {
process((String) object);
}The article also discusses how type erasure affects other generic scenarios, such as implementing Comparator<Integer> . The compiler generates a bridge compare(Object, Object) method that forwards to the type‑specific compare(Integer, Integer) implementation:
public class MyComparator implements Comparator<Integer> {
public int compare(Integer a, Integer b) {
// comparison logic
}
// bridge method
public int compare(Object a, Object b) {
return compare((Integer) a, (Integer) b);
}
}To programmatically distinguish bridge methods, the article points out the Method.isBridge() API, which returns true for synthetic bridge methods. By filtering methods with this call, developers can reliably obtain the actual method signature they need (e.g., the String parameter version).
Further analysis covers additional cases that trigger bridge generation, such as class inheritance where a subclass does not override a method (resulting in a bridge in the subclass) and covariant return types where the overriding method returns a subtype. Examples include a Father class with Object test(String) and a Child class overriding it with String test(String) , which also produces a bridge method.
In summary, bridge methods are a compiler‑generated mechanism to preserve type safety after generic type erasure and covariant returns. Developers can detect them via reflection and use this knowledge to correctly retrieve method parameter types or invoke generic methods at runtime.
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.