查看原文
其他

【Java编程教程】详解Java 协变返回类型

点击关注 👉 Java面试那些事儿 2023-09-01

整理:Java面试那些事儿


协变返回类型指定返回类型可以在与子类相同的方向上变化。


在 Java5 之前,不可能通过改变返回类型来覆盖任何方法。但是现在,从 Java5 开始,如果子类重写任何返回类型为 Non-Primitive 的方法,但它会将其返回类型更改为子类类型,则可以通过更改返回类型来重写方法。让我们举一个简单的例子:


注意:如果您是 Java 初学者,请跳在了解 OOP 概念后再返回阅读本篇教程。


# 协变返回类型的简单示例


文件名:B1.java

class A{ A get(){return this;} }
class B1 extends A{ @Override B1 get(){return this;} void message(){System.out.println("欢迎使用协变返回类型");}
public static void main(String args[]){ new B1().get().message(); } }


输出:

欢迎使用协变返回类型


上面的例子可以看到,A类的get()方法的返回类型是A,B类的get()方法的返回类型是B。两种方法返回类型不同,但都是方法重写. 这称为协变返回类型。关注公众号Java面试那些事儿,获取651页Java面试题


# 协变返回类型的优点


以下是协变返回类型的优点。


  1. 协变返回类型有助于避免类层次结构中令人困惑的类型转换,使代码更易用、可读和可维护。

  2. 在方法覆盖中,协变返回类型提供了拥有更多指向返回类型的自由。

  3. 协变返回类型有助于防止返回时的运行时ClassCastExceptions。


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


文件名:CovariantExample.java

class A1 { A1 foo() { return this; }
void print() { System.out.println("Inside the class A1"); } }

// A2 是 A1 的子类 class A2 extends A1 { @Override A1 foo() { return this; }
void print() { System.out.println("Inside the class A2"); } }
// A3 是 A2 的子类 class A3 extends A2 { @Override A1 foo() { return this; }
@Override void print() { System.out.println("Inside the class A3"); } }
public class CovariantExample { // main方法 public static void main(String argvs[]) { A1 a1 = new A1();
//这样写是没问题的 a1.foo().print();
A2 a2 = new A2();
// 我们需要进行类型转换才能实现 // 读者更清楚创建的对象类型 ((A2)a2.foo()).print();
A3 a3 = new A3();
// 进行类型转换 ((A3)a3.foo()).print(); } }


输出:

Inside the class A1Inside the class A2Inside the class A3


解释:在上面的程序中,A3类继承了A2类,A2类继承了A1类。因此,A1 是类 A2 和 A3 的父类。因此,类 A2 和 A3 的任何对象也是类型 A1。由于方法foo()的返回类型在每个类中都是相同的,我们不知道该方法实际返回的对象的确切类型。我们只能推断返回的对象将是 A1 类型,这是最通用的类。

我们不能确定返回的对象是 A2 还是 A3。这是我们需要进行类型转换以找出从方法foo()返回的对象的特定类型的地方. 它不仅使代码冗长;它还需要程序员的精确度,以确保正确完成类型转换;否则,很有可能获得ClassCastException。


更糟的是,想想层次结构下降到 10 - 15 个类甚至更多的情况,并且在每个类中,方法foo()具有相同的返回类型。这足以让代码的读者和编写者陷入噩梦。关注公众号Java面试那些事儿,获取651页Java面试题


写上面的更好的方法是:


文件名:CovariantExample.java

class A1 { A1 foo() { return this; }
void print() { System.out.println("Inside the class A1"); } }
// A2 是 A1 的子类 class A2 extends A1 { @Override A2 foo() { return this; }
void print() { System.out.println("Inside the class A2"); } }
// A3 是 A2 的子类 class A3 extends A2 { @Override A3 foo() { return this; }
@Override void print() { System.out.println("Inside the class A3"); } }
public class CovariantExample { // main方法 public static void main(String argvs[]) { A1 a1 = new A1(); a1.foo().print(); A2 a2 = new A2(); a2.foo().print(); A3 a3 = new A3(); a3.foo().print(); } }


输出:

Inside the class A1Inside the class A2Inside the class A3


解释:在上面的程序中,不需要类型转换,因为返回类型是特定的。因此,知道从方法foo()返回的对象类型不会造成混淆。此外,即使我们为 10 - 15 个类编写代码,也不会混淆方法的返回类型。由于协变返回类型,所有这一切都是可能的。


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


Java 不允许基于返回类型的重载,但 JVM 总是允许基于返回类型的重载。JVM 使用方法的完整签名来查找/解析。完整签名意味着它除了参数类型之外还包括返回类型。即,一个类可以有两个或多个方法,只是返回类型不同。javac 使用这个事实来实现协变返回类型。



程序员技术交流群


扫码进群记得备注:城市、昵称和技术方向


热门推荐:


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存