2010-04-09 20 views
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是否支持动态方法调用?

This article说:

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

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

我在想什么?

+0

运行该程序时会发生什么? – 2010-04-09 16:46:47

+0

@Dave:打印'b' – Lazer 2010-04-09 16:52:18

+1

该文章的作者需要计算一课。 – Syntactic 2010-04-09 16:56:16

回答

13

你感到困惑动态调用动态绑定 ..

第一个允许类型检查接受你不能确定程序,如果一个方法是存在的物体上时运行时间,而动态绑定只是根据对象的运行时类型选择正确的实现,但保持静态类型检查

这是什么意思?

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

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

仅供参考:invokedynamic功能将与Java7一起添加,因为许多脚本语言已被编写为可在JVM之上工作,并且缺少动态调用功能迫使这些语言的开发人员在脚本以及使用反射来关注动态调用的真正JVM。当然这种方法会导致很多开销(想想Grovvy的MetaClass),这就是为什么Sun决定给他们一个帮助。

+0

为琐事+1 – 2010-04-09 23:59:46

1

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

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

1

事实上,你错过的是这是在文章中解释的'invokevirtual'的一部分。

你只是重写方法,并使用虚拟方法表来调用正确的方法。

0

我不会打电话给你的例子“dynamic”,而是虚拟。因为在编译时,方法名称和签名是已知的(并且它的存在由编译器检查)。唯一在运行时解析的是用于该方法的具体实现。

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

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

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); 
    } 

    // ... 
}