10

这是一个纯粹的理论问题。

给定三个简单的类:

class Base {
}

class Sub extends Base {
}    

class SubSub extends Sub {
}

还有一个旨在对这些类进行操作的函数:

public static void doSomething(Base b) {
  System.out.println("BASE CALLED");
}
public static void doSomething(Sub b) {
  System.out.println("SUB CALLED");
}

似乎以下代码:

SubSub ss = new SubSub();
doSomething(ss);

可能会合法地导致打印 BASE CALLED 或 SUB CALLED,因为 SubSub 可以同时转换为这两者。事实上,删除函数的 Sub 版本会导致打印 BASE CALLED。实际发生的是打印了“SUB CALLED”。这似乎意味着调用哪个函数不取决于定义函数的顺序,因为首先调用了基本版本。

Java 是否只查看函数的所有不同版本并选择需要最小遍历继承堆栈的版本?这是标准化的吗?它是否写在任何文档中?

4

4 回答 4

9

可以在 Java 语言规范 (JLS) 的第 15.12.2.5 部分中找到正式规范。由于泛型,这非常复杂,因此您可能希望查看JLS 第一版的同一部分

它基本上说编译器试图找到一个版本的方法,其中所有参数(包括调用该方法的对象)都是最具体的。如果不存在这样的方法(例如,因为您有method(Base, Sub)andmethod(Sub, Base)但没有method(Sub, Sub)),则编译失败。

请注意,方法的实际选择取决于实例方法的目标对象的动态类型,而不是参数。您的示例在实例级别上仍然可以正常工作。

您应该能够通过强制转换或重新声明ss. 如果声明的变量类型与签名完全匹配,那么对于编译器和维护程序员来说一切都是清楚的。只要声明的类型匹配,您是否随后分配更具体的类型并不重要。

于 2008-12-22T05:14:35.987 回答
2

据我所知,Java 和 C++ 在编译时根据它们可以做出的最具体的匹配来做出这个决定(因为这些是不可动态分派的静态函数)。如果您的静态类型是 SubSub 并且您有一个采用 SubSub 的重载,那么这就是将被调用的重载。我很确定这两种标准都适用。

如果您有 Base 的引用或指针,即使它包含 Sub 或 SubSub,您也会匹配使用 Base 的版本,因为在编译时,这是编译器拥有的唯一保证。

于 2008-12-22T04:31:08.040 回答
0

当您重载静态方法时,它会调用在调用该方法的类中立即定义的方法。但是,如果调用类中没有定义任何方法,那么它将调用从其直接父类继承的方法。

在您的情况下,有两个重载方法都可以接受 SubSub 作为参数。编译器检查最具体的匹配并继续它。但最具体的匹配通常是类型层次结构中最低的。

已编辑

删除了冲突的语句。处于同一类型层次结构级别的类中的两个方法不能处于模棱两可的状态以供编译器选择。这种歧义只有在多重继承的情况下才有可能。

于 2008-12-22T04:51:38.140 回答
0

Java 动态绑定方法(在运行时,取决于对象实例类型,而不是引用类型),但仅在一个方法签名的上下文中。Java 静态绑定方法签名(在编译时)。
换句话说,Java 决定在编译时应该调用什么方法(签名)(基于静态引用的重载)。在运行时,Java 将采用该签名,在对象类型层次结构中找到适当的对象,并在该动态绑定的对象上执行该方法。

重载->什么(编译时的方法签名)
覆盖->从哪里(运行时类型层次结构中的对象)

于 2014-07-19T21:12:56.017 回答