Java是否支持动态方法调用?

3
class A           { void F() { System.out.println("a"); }}
class B extends A { void F() { System.out.println("b"); }}

public class X {
    public static void main(String[] args) {
        A objA = new B();
        objA.F();
    }
}

这里,F()是动态调用吗?

这篇文章说:

......Java字节码不支持动态方法调用。有三种支持的调用模式:invokestatic、invokespecial、invokeinterface或invokevirtual。这些模式允许调用具有已知签名的方法。我们谈论的是强类型语言。这使得在编译时可以直接进行一些检查。

另一方面,动态语言使用动态类型。因此,我们可以调用在编译时未知的方法,但这在Java字节码中完全不可能。

我错过了什么?


当您运行程序时会发生什么? - Dave Jarvis
1
那篇文章的作者需要学习数数的课程。 - Syntactic
5个回答

14
您将 动态调用动态绑定 混淆了。
第一个允许类型检查器接受这样的程序:您不确定在运行时对象上是否存在方法,而动态绑定只是根据对象的运行时类型选择正确的实现,但仍然保持静态类型检查。
这意味着在您的示例中,Java 将调用对象 B 上的实现,因为 objA 变量的运行时类型是 B;并且它将编译,因为它知道 BA,所以方法调用不会在运行时失败(objA 一定有一个 F 的实现)。
相反,使用动态调用时,它不会在编译时检查调用 F 的对象类型是否包含该方法,当然,如果在执行期间指定的对象上不存在该方法,则会引发异常。
另外,有趣的是,invokedynamic 特性将在 Java7 中添加,因为许多脚本语言都是在 JVM 上编写的,缺少动态调用功能迫使这些语言的开发人员在脚本和真正的 JVM 之间添加一个中间层,该层使用反射处理动态调用。 当然,这种方法会导致很多开销(想想 Grovvy 的 MetaClass),这就是 Sun 决定给他们提供帮助的原因。

1
在您的示例中,正确的方法被调用,因为多态地,B 的实例看起来像 A 的实例。可以通过检查对象的运行时类型(即 B)而不是对象引用的编译时类型(A)来定位该方法。另一个重要部分是方法的签名 - 这些必须始终匹配(当然是多态的)。
这与动态语言不同,因为在动态语言中,对象基本上没有编译时 - 一切都必须在运行时解决。

1
事实上,你所忽略的是这是文章中解释的“invokevirtual”的一部分。
你只是覆盖了该方法,并使用虚拟方法表来调用正确的方法。

0

你可以创建功能接口。

class Logger {
  private BiConsumer<Object, Integer> logger = null;

  // ...

  private Logger(Object logger) {
      this.akkaLogger = (LoggingAdapter) logger;
      this.logger = (message, level) -> {
          switch (level) {
              case INFO:  akkaInfo(message);
                          break;
              case DEBUG: akkaDebug(message);
                          break;
              case ERROR: akkaError(message);
                          break;
              case WARN:  akkaWarn(message);
                          break;
          }
      };
  }

  private Logger() {
      this.logger = (message, level) -> System.out.println(message);
  }

  // ...
}

0

我不会称你的例子为“动态”,而是虚拟。因为在编译时,方法名和签名是已知的(并且其存在性由编译器检查)。唯一在运行时解决的是要用于该方法的具体实现。

更恰当的“动态”方法调用示例将涉及反射(请参见Method类)。通过这种方式,可以在运行时调用编译类型未知的方法(这被框架广泛使用,但应用程序代码不太使用)。

你提到的文章在这方面似乎有点误导人。但确实需要知道/检查显式调用的方法的签名在编译时,因此,在这个意义上,Java不是动态的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接