Java 中的协变返回类型

2025 年 3 月 23 日 | 阅读 5 分钟

协变返回类型指定返回类型可能在子类中变化(向同一方向变化)。

在 Java 5 之前,不可能通过更改返回类型来重写任何方法。从 Java 5 或更高版本开始,如果子类重写了任何返回类型为非原始类型的父类方法,并将其返回类型更改为子类类型,那么就可以通过更改返回类型来重写方法。让我们看一个简单的例子

注意:如果您是 Java 初学者,请跳过此主题,并在学习完 OOPs 概念后再返回讨论。

协变返回类型示例

文件名: B1.java

输出

Welcome to the covariant return type

如上例所示,A 类的 get() 方法的返回类型是 A,而 B 类的 get() 方法的返回类型是 B。这两个方法具有不同的返回类型,但这却是方法重写。这就是所谓的协变返回类型。

协变返回类型的优点

  1. 协变返回类型有助于避免类层次结构中令人混淆的类型转换,使代码更可用、可读、可维护。
  2. 在方法重写中,协变返回类型提供了更精确的返回类型的自由。
  3. 协变返回类型有助于防止运行时ClassCastExceptions

让我们看一个例子来理解协变返回类型的优点

文件名:CovariantExample.java

示例

编译并运行

输出

Inside the class A1
Inside the class A2
Inside the class A3

说明: 在上面的程序中,类 A3 继承类 A2,类 A2 继承类 A1。因此,A1 是类 A2 和 A3 的父类。因此,类 A2 和 A3 的任何对象也都是 A1 类型。由于每个类中foo()方法的返回类型相同,我们不知道该方法实际返回对象的确切类型。

我们只能推断返回的对象将是 A1 类型,这是最通用的类。我们不能确定返回的对象是 A2 还是 A3。这时就需要进行类型转换来找出从foo()方法返回的对象的特定类型。

这不仅使代码冗长。它还要求程序员精确地确保类型转换正确进行;否则,很有可能出现ClassCastException

更糟糕的是,设想一个情况,继承结构向下延伸到 10-15 个甚至更多的类,并且在每个类中,foo()方法都有相同的返回类型。这足以让代码的读者和编写者做噩梦。

更好的编写方式如下

文件名:CovariantExample.java

示例

编译并运行

输出

Inside the class A1
Inside the class A2
Inside the class A3

说明: 在上面的程序中,不需要进行类型转换,因为返回类型是特定的。因此,在了解从foo()方法返回的对象类型方面没有任何混淆。此外,即使我们为 10-15 个类编写代码,关于方法的返回类型也不会有任何混淆。这一切都归功于协变返回类型。

协变返回类型是如何实现的?

JVM 始终允许基于返回类型的重载。JVM 使用方法的完整签名进行查找/解析。完整签名是指除了参数类型之外,还包括返回类型。即,一个类可以有两个或多个仅返回类型不同的方法。javac 利用这一事实来实现协变返回类型。

换句话说,协变返回类型是通过允许子类方法返回超类方法返回类型的子类型来实现的。子类方法必须具有与超类方法相同的签名,但可以返回更具体的类型。

Java 中使用协变返回类型进行方法重写

如果一个类中的方法返回非原始类型,那么该类子类中的同一个方法可以使用相同的非原始类型或该非原始类型的子类型作为返回类型来重写该方法。

如果类 A 有一个方法的返回类型是 Object 类型,那么父类 A 的子类 B 中的同一个方法可以使用 Number 或 Integer 作为返回类型来重写该方法,因为 Number 和 Integer 类是 Object 类的协变。

让我们通过一个例子来理解。

文件名:B.java

示例

输出

print method of class B
print method of class B

如上程序所示,类 B 中的 print() 方法重写了父类 A 的 print() 方法,尽管子类方法的返回类型是 Integer 类型。这是因为 Java 允许在重写方法时使用协变返回类型。