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

时间:2010-04-09 16:41:33

标签: java dynamic bytecode dynamic-method

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()是动态调用的,不是吗?

This article说:

  

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

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

我缺少什么?

5 个答案:

答案 0 :(得分:13)

您对动态绑定动态调用感到困惑。

第一个允许类型检查器接受程序,在这些程序中,您不确定在运行时是否在对象上存在方法,而动态绑定只是根据对象的运行时类型选择正确的实现< strong>但保持静态类型检查。

这是什么意思?

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

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

只是为了琐事:F功能将添加到Java7中,因为许多脚本语言已编写为在JVM之上工作,缺乏动态调用功能迫使这些语言的开发人员添加中间脚本和真正的JVM之间的层,它关注使用反射的动态调用。当然这种方法会带来很多开销(想想Grovvy的invokedynamic),这就是为什么Sun决定给他们一个帮助..

答案 1 :(得分:1)

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

这与动态语言不同,因为那些对象基本上没有编译时间 - 所有内容都必须在运行时解析。

答案 2 :(得分:1)

事实上,你所缺少的是这是“invokevirtual”的一部分,文章对此进行了解释。

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

答案 3 :(得分:0)

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

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

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

答案 4 :(得分: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);
  }

  // ...
}