1

维基百科指出:

由于在运行前(通常)不知道多态对象的具体类型,因此执行的函数是动态绑定的。以下面的 Java 代码为例:

 public void foo(java.util.List<String> list) {
     list.add("bar");  
 }

List 是一个接口,所以 list 必须引用它的子类型。它是对 LinkedList、ArrayList 还是其他 List 子类型的引用?add 引用的实际方法直到运行时才知道。

考虑这个例子:

List<String> list;

list = new LinkedList<String>();
foo(list);

list = new ArrayList<String>();
foo(list);

为什么这里引用的实际方法直到运行时才知道?编译器不能只检查对象列表分配给哪种类型的 foo 的每个调用吗?当然,这只有在程序是确定性的并且不涉及随机性(例如用户交互)时才有可能。

这是引用声明中的(一般)是关于还是我的理解错误?

在程序是确定性的特殊情况下,是使用静态绑定还是 - 在 Java 中 - 总是使用动态绑定,不管有什么可能?如果是这样,为什么?

4

2 回答 2

2

该声明谈到了一般情况。仅给出 Wikipedia 示例的代码,无法判断list参数的具体类型。在您的示例中,可以告诉具体类型。

Java 运行时是允许的,如果它可以检测到变量的具体类型,它实际上会去虚拟化方法调用。

如果您对该主题感兴趣:这里是讨论去虚拟化技术的论文链接。

于 2015-10-11T09:53:16.137 回答
1

在将 java 源代码编译为 java 字节码的过程中,不进行去虚拟化。否则这将是非常脆弱的。请注意,编译后的 java 类通常保持二进制兼容性(有一些已知的例外)。因此,如果您foo位于单独的类中并且您只重新编译这个类,那么类调用foo应该可以使用新代码而无需重新编译。

然而,去虚拟化在运行时是可能的,并且实际上由大多数现代 JVM(包括 Oracle HotSpot JVM,或当然)执行。该方法很可能在 JIT 编译期间完全内联:foo调用LinkedList.addArrayList.add方法都将合并到调用者方法的主体中。

所以总的来说维基百科引用是正确的:实际引用的方法add直到运行时才知道。然而,这并不意味着调用仍然是多态的,因为 JVM 运行时是相当复杂的事情,包括解释器、JIT 编译和 JIT 编译代码的执行。

于 2015-10-11T10:14:48.177 回答