这是一个纯粹的理论问题。
给出三个简单的类:
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”。这似乎意味着调用哪个函数不依赖于定义函数的顺序,因为首先调用了Base版本。
Java只是查看函数的所有不同版本并选择需要最小遍历继承堆栈的函数吗?这标准化了吗?它是在任何文档中写出来的吗?
答案 0 :(得分:8)
正式规范可在part 15.12.2.5 of the Java Language Specification (JLS)中找到。感谢泛型,这非常复杂,因此您可能需要查看same section of the first edition of the JLS。
它基本上说编译器试图找到该方法的一个版本,其中包括调用该方法的对象的所有参数都是最具体的。如果不存在此类方法(例如,因为您有method(Base, Sub)
和method(Sub, Base)
而不是method(Sub, Sub)
),则编译失败。
请注意,方法的实际选择取决于实例方法的目标对象的动态类型,但不取决于参数。您的示例在实例级别上仍然可以正常工作。
您应该能够通过强制转换或重新声明ss
的类型来为编译器提供帮助。如果声明的变量类型与签名完全匹配,那么编译器和维护程序员也清楚一切。只要声明的类型匹配,如果您再分配更具体的类型并不重要。
答案 1 :(得分:2)
据我所知,Java和C ++在编译时做出这个决定(因为这些是不可动态调度的静态函数),这取决于它们可以进行的最具体的匹配。如果您的静态类型是SubSub并且您有一个带SubSub的重载,那么这将是将被调用的。我很确定它符合两种标准。
如果你有一个Base的引用或指针,即使它包含Sub或SubSub,你将匹配带Base的版本,因为在编译时,这是编译器唯一的保证。
答案 2 :(得分:0)
当您重载静态方法时,它会调用在调用该方法的类中立即定义的方法。但是,如果调用类中没有定义任何方法,那么它将调用从其直接父类继承的方法。
在您的情况下,有两个重载方法,它们都可以接受SubSub作为参数。编译器检查最具体的匹配并为其进行。但最具体的匹配通常是类型层次结构中最低的。
<强> EDITED 强>
删除了冲突的声明。处于相同类型层次级别的类中的两个方法不能处于模糊状态以供编译器选择。只有在多重继承的情况下才可能出现这种歧义。
答案 3 :(得分:0)
Java动态绑定方法(在运行时,取决于对象实例类型,而不是引用类型),但仅在一个方法签名的上下文中绑定。 Java静态绑定方法签名(在编译时)。
换句话说,Java决定在编译时应该调用哪种方法(签名)(静态 - 基于引用 - 重载)。在运行时,Java将采用该签名,在对象类型层次结构中找到适当的对象,并在该动态绑定对象上执行该方法。
重载 - &gt;什么(编译时的方法签名)
覆盖 - &gt;从哪里(运行时类型层次结构中的对象)