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 字节码是完全不可能的。

我错过了什么?

4

5 回答 5

14

您将动态调用动态绑定混淆..

第一个允许类型检查器接受您不确定方法是否会在运行时出现在对象上的程序,而动态绑定只是根据对象的运行时类型选择正确的实现,但静态维护类型检查

这是什么意思?

这意味着在您的示例中,Java 将调用对象上的实现,B因为objA变量的运行时类型是B; 它会编译,因为它知道 aB 是 a A所以方法调用在运行时不会失败(肯定objA会有一个F实现)。

使用动态调用,它不会在编译时检查您正在调用的对象的类型是否F包含该方法,当然如果在执行期间该方法在指定对象上不可用,它将引发异常。

只是为了琐事:该invokedynamic功能将与 Java7 一起添加,因为许多脚本语言已被编写为在 JVM 之上工作,并且缺乏动态调用功能迫使这些语言的开发人员在脚本和真正的 JVM 之间添加一个中间层关心使用反射的动态调用。当然,这种方法会导致大量开销(想想 Grovvy 的MetaClass),这就是 Sun 决定帮助他们的原因。

于 2010-04-09T16:48:50.783 回答
1

在您的示例中,调用了正确的方法,因为 B 的实例在多态上看起来像 A 的实例。可以通过检查对象的运行时类型来定位该方法;即,B;与对象引用的编译时类型相反,A. 另一个重要部分是方法的签名 - 这些必须始终匹配(当然是多态的)。

这与动态语言不同,因为在那些语言中基本上没有对象的编译时间 - 一切都必须在运行时解决。

于 2010-04-09T16:49:23.110 回答
1

实际上,您缺少的是,这是文章中解释的“invokevirtual”的一部分。

您只是覆盖该方法并使用虚拟方法表来调用正确的方法。

于 2010-04-09T16:50:20.453 回答
0

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

一个更恰当的“动态”方法调用示例将涉及反射(参见 Method 类)。这样,可以在运行时调用编译类型未知的方法(这在框架中广泛使用,而不是在应用程序代码中使用)。

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

于 2010-04-09T16:54:04.907 回答
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);
  }

  // ...
}
于 2015-11-16T00:32:08.050 回答