协变返回类型机制允许在 Java 中重写方法时返回的类型不是与超类完全相同的类型,而是它的子类型(covariant type)。
这提高了代码的表达能力,可以在不显式转换的情况下获得更具体的类型。
示例:
class Animal { } class Dog extends Animal { } class Parent { Animal getAnimal() { return new Animal(); } } class Child extends Parent { @Override Dog getAnimal() { return new Dog(); } // 协变返回 }
工作的条件:
在子类中是否可以改变重载方法的返回类型,使其成为基类型的子类型?这算不算有效的重载?
答案: 不可以,对于重载(overloading),只考虑参数的签名,返回类型并不重要。单独更改重载中的返回类型是不可行的——这样的办法会导致签名冲突。
示例:
class Example { Animal make() { return new Animal(); } // Dog make() { return new Dog(); } // 编译错误:重复的方法! }
协变性仅适用于重写(overriding)。
历史
在项目中,程序员犯了错,添加了同名及相同参数的重载方法,但返回类型不同,期望这是“协变重写”。结果导致项目无法编译,而这个bug在 CI 中才被发现。
历史
在类的层次结构中使用协变返回时,开发人员错误地应用了参数化类型(generics),未正确实现子类型,导致在处理集合时出现运行时的 ClassCastException 错误。
历史
在重写的方法中返回了更具体的类型,但团队没有记录该契约。在通过基类型工作的代码中,类型转换遇到困难,并在“上转型”引用时出现意外行为,导致了一系列隐藏的错误。