Java方法重写要求返回值类型兼容:基本类型和void必须完全一致;引用类型支持协变返回,子类可返回父类返回类型的子类,如Number→Integer、Collection→ArrayList;泛型协变依赖桥接方法实现。

Java 中方法重写对返回值的兼容性有明确规则:基本类型和 void 必须完全一致;引用类型允许协变(covariant),即子类可返回父类返回类型的子类。
基本类型和 void 必须严格相同
如果父类方法返回 int、boolean 或 void,子类重写时不能更改——哪怕只是换成包装类(如 int → Integer)也不行,编译直接报错。
-
正确:
public int getId()→@Override public int getId() -
错误:
public int getId()→@Override public Integer getId()(类型不兼容)
引用类型支持协变返回
当父类方法返回引用类型(如 Number、Object、List 等),子类可缩小返回范围,返回其具体子类。这是 Java 5 引入的重要特性,体现“更具体的语义更安全”原则。
-
正确:
public Number getValue()→@Override public Integer getValue() -
正确:
public Collection<String> getItems()→@Override public ArrayList<String> getItems() -
错误:
public String getName()→@Override public Object getName()(扩大返回范围,违反里氏替换)
桥接方法保障 JVM 层面调用正确
协变返回在字节码层面无法直接表达(JVM 方法签名只认 descriptor)。编译器会自动生成一个桥接方法(ACC_BRIDGE + ACC_SYNTHETIC),签名与父类擦除后一致,并转发到子类真实方法。
立即学习“Java免费学习笔记(深入)”;
- 例如父类
public Number getValue(),子类写public Integer getValue() - 编译后子类实际含两个方法:
Integer getValue()(源码可见)和Number getValue()(桥接,不可见) - 通过父类引用调用时,JVM 匹配到桥接方法,再跳转至真实方法,多态正常生效
泛型方法的协变也依赖桥接
泛型擦除会导致签名断裂。比如父类 public <T> T get() 擦除为 public Object get(),子类若写 @Override public String get(),编译器同样生成桥接方法 public Object get(),内部强制转型并调用 String get()。
- 这说明协变不是语法糖,而是编译器+JVM 协同保障的底层机制
- 反射获取桥接方法的
getReturnType()仍是Object.class或Number.class,不暴露子类真实类型


















