【问题标题】:Does Java support dynamic method invocation?Java 是否支持动态方法调用?
【发布时间】:2010-04-09 16:41:32
【问题描述】:
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 字节码不支持
动态方法调用。有
三种支持的调用模式:
调用静态,调用特殊,
调用接口或调用虚拟。
这些模式允许调用方法
具有已知签名。我们谈论
强类型语言。这允许
直接在
编译时间。
另一方面,动态
语言使用动态类型。所以我们可以
在编译时调用未知的方法
时间,但这完全不可能
使用 Java 字节码。
我错过了什么?
【问题讨论】:
标签:
java
dynamic
bytecode
dynamic-method
【解决方案1】:
您将 动态调用 与 动态绑定..
混淆了
第一个允许类型检查器接受您不确定在运行时对象上是否存在方法的程序,而动态绑定只是根据对象的运行时类型选择正确的实现 但保持静态类型检查。
什么意思?
这意味着在您的示例中,Java 将调用对象B 的实现,因为objA 变量的运行时类型是B;它会编译,因为它知道 B 是 A 所以方法调用在运行时不会失败(objA 肯定会有 F 实现)。
使用动态调用,它不会在编译时检查您正在调用F 的对象的类型是否包含该方法,当然如果在执行期间该方法不可用,它会引发异常在指定的对象上。
只是为了琐事:invokedynamic 功能将与 Java7 一起添加,因为许多脚本语言已被编写为在 JVM 之上工作,并且缺乏动态调用功能迫使这些语言的开发人员在它们之间添加一个中间层脚本和真正的 JVM 关心使用反射的动态调用。当然,这种方法会导致大量开销(想想 Grovvy 的 MetaClass),这就是 Sun 决定帮助他们的原因..
【解决方案2】:
在您的示例中,调用了正确的方法,因为 B 的实例在多态上看起来像 A 的实例。可以通过检查对象的运行时类型来定位该方法;即,B;与对象引用的编译时类型相反,A. 另一个重要部分是方法的签名 - 这些必须始终匹配(当然是多态的)。
这与动态语言不同,因为在动态语言中,对象基本上没有编译时间 - 一切都必须在运行时解决。
【解决方案3】:
事实上,您缺少的是,这是文章中解释的“invokevirtual”的一部分。
您只是重写了方法,并使用虚拟方法表来调用正确的方法。
【解决方案4】:
我不会将您的示例称为“动态”,而是虚拟。因为在编译时方法名称和签名是已知的(编译器会检查它的存在)。唯一在运行时解决的是用于该方法的具体实现。
更恰当的“动态”方法调用示例将涉及反射,(请参阅 Method 类)。这样,可以在运行时调用编译类型未知的方法(这在框架中广泛使用,而不是在应用程序代码中使用)。
您提到的文章在这方面似乎有点误导。但是确实必须在编译时知道/检查您显式调用的方法的签名,因此,从这个意义上说,Java 不是动态的。
【解决方案5】:
您可以制作功能界面。
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);
}
// ...
}