3

假设以下现实生活中的代码结构:

interface I {
  I m();
}

abstract class A implements I {
  @Override
  public abstract A m(); 

  abstract class B extends A {

  }
}

为 B 生成的字节码是

abstract class A$B extends A {
  <some stuff>

  public I m(); //bridge method
    Code:
       0: aload_0
       1: invokespecial #2                  // Method A.m:()LA;
       4: areturn
}

注意invokespecial 指令指向抽象方法Am() 的使用。在我看来,这必须在运行时根据 JVM 8 规范在invokespecial上导致 AbstractMethodError :

如果以下所有条件都为真,则令 C 为当前类的直接超类。

因此,在我们的示例中,A 将被选为 C。

要调用的实际方法由以下查找过程选择。

1) 如果 C 包含与已解析方法具有相同名称和描述符的实例方法的声明,那么它就是要调用的方法。

所以JVM会选择Am()。

但是运行时异常部分指出:

否则,如果查找过程的第 1 步、第 2 步或第 3 步选择了一个抽象方法,invokespecial 将抛出一个 AbstractMethodError。

因此调用该方法将导致错误。

我只是想知道,为什么 Java 编译器会生成这样的被判失败的字节码?

PS 我猜测上面的桥接方法根本不会被调用,因为 AB 类的最终实现者将覆盖它。但是接下来就出现了下面的问题:java生成这个桥接方法是为了什么目的?

4

1 回答 1

0

我真的不明白你想达到什么目的。您想了解为什么要生成该方法吗?

注意invokespecial 指令指向抽象方法Am() 的使用。在我看来,这必须在运行时导致 AbstractMethodError

理论上不应该有抽象类的实例,任何被实例化的子类都必须实现所有抽象方法。所以invokespecial指向实现子类。尝试实例化抽象类通常会被编译器捕获。

当没有找到实现子类时抛出 AbstractMethodError ,大多数时候它发生在按预期加载不同的类版本或使用字节码操作时。

于 2017-03-03T08:47:17.917 回答