首页 > 解决方案 > 在 Java 字节码/类格式中,是什么决定了一个方法是否覆盖另一个方法?

问题描述

我知道字节码规范允许类具有具有相同签名的方法,仅在返回类型上有所不同,这与 Java 语言不同。有些语言甚至在某些情况下使用它。我的问题与反思有关:

如果在一个类中我找到一个(非私有)方法,其名称和参数类型与其超类(非最终,非私有)相同,并且返回类型等于或是所述返回类型的子类型超类中的方法,我什么时候可以假设静态调用“超方法”的代码将始终导致执行“覆盖(?)”方法(自然假设调用是在该类的对象上进行的)?即使在其他语言编译为 JVM 字节码的情况下,或者如果涉及运行时代码生成,或者在像 lambda 转发器这样的被黑客入侵的合成类中?

我的问题是从注意到 Scala 标准库中的Iterable[E]一个方法引起的:

def map[O](f :E => O) :Iterable[E]

而 aMap[K, V]的子类Iterable[(K, V)]声明了另一个方法:

def map[A, B](f :((K, V)) => (A, B)) :Map[A, B]

实际的签名比这里复杂,但原理是一样的:擦除后,in 的方法Map可以覆盖 in 的方法Iterable

标签: javajvmbytecodejvm-bytecode

解决方案


覆盖的事实由 JVM 通过方法描述符(包括参数类型返回类型)的精确相等性来确定。请参阅 JVMS §5.4.5§5.4.6

因此,在 JVM 级别,返回的方法Map不会覆盖返回的方法Iterable。编译器通常会生成一个桥接方法返回Iterable,通过对返回方法的简单委托来实现Map

例子:

class A<T extends Iterable> {
    T map() {
        return null;
    }
}

class B extends A<Collection> {
    @Override
    Collection map() {
        return null;
    }
}

此处B.map覆盖A.map,即使返回类型不同。为了使这种层次结构成为可能和有效,编译器(在字节码级别)生成另一个B.map返回的方法Iterable

class B extends A<java.util.Collection> {
  B();
       0: aload_0
       1: invokespecial #1                  // Method A."<init>":()V
       4: return


  java.util.Collection map();
       0: aconst_null
       1: areturn


  java.lang.Iterable map();
       0: aload_0
       1: invokevirtual #7                  // Method map:()Ljava/util/Collection;
       4: areturn

A.map在 的实例上调用虚拟方法时,总是会调用B一个方法,而该方法又会调用.B.map:IterableB.map:Collection


推荐阅读