Java 5 支持协变返回类型,即子类重写父类方法时可将返回类型替换为更具体的子类型;要求父类返回引用类型,子类返回其子类,方法签名其余部分必须一致,且仅适用于直接继承关系,不适用于泛型类型参数。

子类重写方法返回更具体的类型,Java 5 就支持了
Java 中允许子类重写父类方法时,把返回类型换成它的子类型——这就是协变返回类型(covariant return type)。它不是语法糖,是编译器层面的合法放宽,不需要强制转型,也不影响多态调用。
getOwner() 返回 Person,子类里改成返回 Student 就行
典型场景:父类定义通用行为,子类细化语义。比如一个 Document 类有 getOwner() 方法返回 Person,而 SchoolDocument 的拥有者一定是 Student,这时直接改返回类型更安全、更清晰。
实操要点:
- 父类方法返回类型必须是引用类型(不能是基本类型或
void) - 子类重写方法的返回类型必须是父类对应方法返回类型的子类(或相同类型)
- 方法签名其他部分(名称、参数列表、异常声明)必须完全一致
- 不加
@Override注解也能编译通过,但强烈建议加上,避免意外重载
示例:
立即学习“Java免费学习笔记(深入)”;
class Person { }
class Student extends Person { }
class Document {
Person getOwner() { return new Person(); }
}
class SchoolDocument extends Document {
@Override
Student getOwner() { return new Student(); } // ✅ 合法协变
}
别在泛型擦除后“假装”协变:List<String> 不能协变为 ArrayList<String>
协变只作用于**直接的类继承关系**,不适用于泛型类型参数。常见误解是以为 List<String> 和 ArrayList<String> 能构成协变——其实不能,因为 ArrayList 是 List 的实现类,但 List<String> 本身不是类型,擦除后都是 List。
容易踩的坑:
- 写
ArrayList<String> getList()去重写父类的List<String> getList()?✅ 可以,因为ArrayList是List的子类 - 写
ArrayList<Object> getList()去重写ArrayList<String>?❌ 不行,泛型类型不参与继承判断,ArrayList<Object>和ArrayList<String>没有父子关系 - 用原始类型(如
ArrayList)去协变?⚠️ 虽然能编译,但失去泛型检查,不推荐
IDE 和编译器对协变的支持很稳,但运行时没特殊行为
协变是纯编译期特性。字节码里子类方法的签名仍会保留桥接方法(bridge method),由编译器自动生成,确保 JVM 多态调用不崩。你不用管它,但得知道:这不是运行时魔法,也不是反射能绕开的限制。
注意点:
- 用反射获取
Method.getReturnType()会返回你写的那个具体类型(比如Student),不是父类的Person - 如果父类方法是
final,协变不生效——根本没法重写 - 接口默认方法也支持协变返回,规则和类一致
真正容易被忽略的是:协变只解决“返回类型变窄”,不解决“参数类型变宽”。想参数也协变?那是逆变,Java 不支持(除了通配符 ? super T 在特定上下文)。










