协变返回类型指子类重写父类方法时可将返回类型声明为父类返回类型的子类型,Java 5起支持,要求方法签名一致、返回类型为子类且父类方法非private或static。

Java 中子类重写方法时使用协变返回类型,核心是让子类方法返回比父类更具体的引用类型,同时保持方法签名完全一致。这从 Java 5 开始原生支持,不需要额外配置,是编译期检查的语法特性。
方法签名必须严格相同
子类重写的方法名、参数数量、参数类型和顺序,必须与父类方法一模一样。编译器只看擦除后的签名(即忽略泛型),不支持参数类型的协变或逆变。
- ✅ 正确:
Animal getPet()→ 子类重写为Dog getPet() - ❌ 错误:
void feed(Animal a)→ 改成void feed(Dog d),这属于重载,不是重写,也不满足里氏替换原则 - ⚠️ 注意:泛型在字节码中被擦除,
List<Dog>和List<Animal>擦除后都是List,不能仅靠泛型区分重写
返回类型只能是父类返回类型的子类(引用类型)
协变只作用于返回值位置,且要求父类返回的是引用类型(不能是基本类型、void 或类型变量本身)。子类返回类型必须是父类返回类型的子类或相同类型。
- ✅ 合法:
Number → Integer、Collection → ArrayList、Iterator → ListIterator - ❌ 不合法:
int → Integer(基本类型与包装类无继承关系)、void → String、Object → String(若父类返回的是Object,子类可返回String;但若父类方法签名是Object get(),而子类写String get(),前提是父类方法本身允许被重写——即非 private/static)
配合多态调用,避免强转
协变的价值在多态调用中真正体现:用父类引用接收结果,却能直接使用子类特有方法,无需显式强制转换。
立即学习“Java免费学习笔记(深入)”;
- 例如:
ShapeFactory factory = new CircleFactory(); Circle c = factory.create();—— 编译通过,c.draw()可直接调用 - 再如:
Collection<String> list = new ArrayList<>(); ListIterator<String> it = list.iterator();—— 因为ArrayList.iterator()协变返回ListIterator,持有ArrayList引用时可直接调用hasPrevious() - 关键点:编译器根据引用的**静态类型**决定可访问成员;协变让这个静态类型信息更精确,提升安全性与 IDE 提示质量
接口实现中的“伪协变”依赖桥接方法
接口本身不支持协变返回类型的声明(implements 不是 extends),但实现类可以写具体返回类型。这时编译器会自动生成桥接方法,保障多态调用正确路由。
- 例如:
interface Container<T> { T get(); },实现类class StringContainer implements Container<String> { public String get() { ... } } - 编译后,
StringContainer实际包含两个方法:public String get()(你写的)和public Object get()(桥接方法,内部调用前者) - 这样,当用
Container<String> c = new StringContainer(); Object o = c.get();时,JVM 能通过桥接方法满足接口契约,同时运行时返回真实字符串


















