Java从5开始支持协变返回类型,即子类重写父类方法时可将返回类型缩小为更具体的子类,提升类型安全与API表达力;仅适用于引用类型,需显式@Override,不支持基本类型、void及泛型擦除后无继承关系的类型。

Java 方法重写支持协变返回类型,是指子类在重写父类方法时,可以将返回值类型声明为父类返回类型的**更具体的子类**,只要满足编译期类型兼容规则。这不是语法糖,而是从 Java 5 起正式支持的语言特性,核心目标是提升类型安全性和 API 表达力。
协变返回类型的基本写法
关键在于:方法名、参数列表、访问修饰符(不能更严格)必须与父类完全一致,仅返回类型可“缩小”为子类型。
- 父类方法返回 Animal,子类可返回 Dog(Dog extends Animal)
- 父类返回 Number,子类可返回 Integer 或 Double
- 父类返回 Object,子类可返回任意引用类型(如 String、List 等),但必须是其子类型(所有类都继承自 Object)
- 必须显式添加 @Override 注解,否则编译器无法识别这是重写,可能误判为错误或重载
哪些情况不支持协变
协变只适用于引用类型,有明确边界:
- 基本类型(int、boolean、char 等)不支持协变:子类必须严格返回相同类型
- void 不支持协变:只能保持 void
-
泛型类型擦除后不构成继承关系的,不算协变:List
和 List - 返回类型扩大(如父类返回 Dog,子类返回 Animal)是非法的:这违反里氏替换原则,编译直接报错
接口继承链中协变生效的前提
协变返回类型不是自动穿透整个继承体系的——它依赖编译器能否在调用点识别出当前静态类型已声明协变签名。
立即学习“Java免费学习笔记(深入)”;
- 如果父接口定义了
EntityId clone(),子接口Ticket extends EntityId没有重新声明该方法,那么变量类型为Ticket时调用clone(),编译器仍按EntityId推断返回类型 - 解决办法是:在子接口中显式重声明方法,例如
@Override Ticket clone();,这样编译器就知道该路径下返回类型已协变为 Ticket - 实现类中再用
@Override返回具体类型(如TicketImpl),整个链条就完整了
为什么能用、怎么受益
协变返回的本质是:子类承诺“我返回的东西,一定是父类说的那种东西,而且往往更具体”。这带来三方面好处:
-
避免强制转型:用子类引用调用时,直接拿到精确类型,比如
Dog d = new Dog().getOwner(); -
保障多态安全:用父类引用调用时,仍可接收返回值(向上转型自然成立),比如
Animal a = new Dog().getOwner(); -
支撑流式 API 设计:抽象基类返回自身类型(
Builder),子类返回具体构建器(HttpBuilder),链式调用不中断、类型不丢失


















