我确实理解创建桥接方法的需要,例如期望传递参数的setter方法,但是getter方法呢?为什么Java也会产生桥方法呢?

以下是ChatGPT生成的一个虚拟代码,目的是让事情变得更具体:

class Box<T> {
private T value;

public void setValue(T value) {
    this.value = value;
}

public T getValue() {
    return value;
}
}

class StringBox extends Box<String> {
public void setValue(String value) {
    System.out.println("Setting a String value in StringBox");
    super.setValue(value.toString());
}

public String getValue() {
    System.out.println("Getting String value from StringBox");
    return super.getValue();
}
}

擦除后的Box类将具有以下方法: public Object getValue();个 在StringBox类中,我们定义了方法: public String getValue() 但这两个方法具有相同的签名,并且覆盖方法的返回类型使用字符串作为返回类型,它是Object类(协变返回类型)的子类 因此,覆盖似乎已经完成.为什么需要桥接法? 我遗漏了什么?

推荐答案

实际上,JVM不实现返回类型协方差.即使没有泛型,Java也需要桥接方法来在JVM上实现返回类型协变.如果编译以下代码,

class MySupplier {
    public Object supply() { return null; }
}
class HelloSupplier extends MySupplier {
    @Override public String supply() { return "Hello, World!"; }
}

您会发现HelloSupplier包含的字节码如下所示:

class HelloSupplier extends MySupplier {
    public String supply() { return "Hello, World!"; }
    public /*bridge*/ Object supply() { return this.supply()/*String*/; }
    // in bytecode, all method calls include the signature of the method, including the return type (which is written after the method name and parameters)
    // the bridge method supply()Object calls the method supply()String
    // note that bytecode allows overloading on return type, even though Java doesn't
}

如果你看一下覆盖的JVM的定义,你会注意到它不同于Java的S关于覆盖的 idea ,因为在JVM的眼中,覆盖的签名必须是exactly.您不必放松参数类型或加强返回类型.

JVM Specification SE 20, Section 5.4.5

实例方法mC可以覆盖另一实例方法mA当以下所有条件均为真时:

  • mCmA具有相同的名称和描述符.
  • ...

这一点很重要,因为这是JVM在执行虚拟查找时决定调用哪个方法的方式.在HelloSupplier对象上对MySupplier#supply()Object的虚拟调用(invokevirtual)永远不会调用HelloSupplier#supply()String,因为在JVM看来,supply()String方法不会覆盖supply()Object方法(即使它在Java中是覆盖的).这样的虚方法调用将调用HelloSupplier#supply()Object(如果它存在),或者在超类中查找这样的方法.为了实现返回类型协方差,Java编译器必须以JVM理解的唯一方式覆盖MySupplier#supply()Object:通过定义HelloSupplier#supply()Object.

因为JVM无论如何都不实现返回类型协变,所以Java编译器不能利用返回类型协变来避免发出泛型代码的桥.

Java相关问答推荐

Spring Webocket:尽管凭据设置为False,但MLhttpsify和Fetch请求之间的CORS行为存在差异

如何使用解析器组合子解析Java数组类型签名?

Java自定义ThreadPool—暂停任务提交并取消当前排队任务

从技术上讲,OPC UA客户端是否可以通过转发代理将请求通过 tunel 发送到OPC UA服务器?

使用java访问具体子类特定方法的最佳方法是什么?

通过Spring Security公开Spring Boot执行器端点

在Eclipse中数组的可空性

舰队运行配置Maven版本

Spring Boot中的应用程序.properties文件中未使用的属性

一对多关系和ID生成

如何使用MapStrCut转换双向链接

IntelliJ IDEA依赖项工具窗口丢失

判断重复的两个二维表算法?

在应用程序运行时更改LookAndFeel

如何在Maven Central上部署?

如何通过gradle命令行从build.gradle获得Java targetCompatibility

如何判断元素计数并在流的中间抛出异常?

Android上的SQLite:Android.database.SQLite.SQLiteReadOnlyDatabaseException:try 写入只读数据库(代码1032 SQLite_readonly_DBMOVED)

Java编译器是否进行了持续的折叠优化,以及如何进行判断?

睡眠在 Spring Boot 中