鉴于我有一个包含两个构造函数的类:
public class TestClass {
ObjectOne o1;
ObjectTwo o2;
public TestClass(ObjectOne o1) {
// ..
}
public TestClass(ObjectTwo o2) {
// ..
}
}
请假设ObjectOne
是interface
类型,ObjectTwo implements ObjectOne
。如果我打电话,会发生什么:
new TestClass(null);
如何确定要调用的正确方法?谁决定了? Java和其他OOP语言之间是否存在差异?
答案 0 :(得分:16)
这个问题实际上是关于解决模糊的重载,而不是真正的运行时多态(因为选择要调用的重载方法是在编译时完成的)。
方法解析是一件复杂的事情,在这种情况下,代码是否完全编译取决于所涉及的类型。 有很多情况下代码会编译。请参阅下面的代码(请注意String implements CharSequence
):
public class MyClass {
MyClass(CharSequence charSeq) {
System.out.println("CharSequence");
}
MyClass(String s) {
System.out.println("String");
}
public static void main(String[] args) {
new MyClass(null); // prints "String"
new MyClass(""); // prints "String"
new MyClass((CharSequence) null); // prints "CharSequence"
new MyClass((CharSequence) ""); // prints "CharSequence"
}
}
请注意,如果没有强制转换,则会选择String
重载。这与JLS中指定的完全相同:
如果多个成员方法都可访问并适用于方法调用,则必须选择一个为运行时方法调度提供描述符。 Java编程语言使用选择最具体方法的规则。
String
是CharSequence
,但并非所有CharSequence
都是String
。因此,String
更具体而不是CharSequence
,因此在上面的示例中选择String
重载的原因。
值得注意的是这个确切的问题(实质上)出现在精彩的Java Puzzlers(强烈推荐)中,特别是 Puzzle 46:令人困惑的构造函数的案例< / em>的
Java的重载解析过程分两个阶段进行。第一阶段选择可访问和适用的所有方法或构造函数。第二阶段选择在第一阶段中选择的方法或构造函数的最具体的。如果一个方法或构造函数可以接受传递给另一个
的任何参数,那么它的特定性不如另一个理解这个难题的关键是哪个方法或构造函数最具体的测试不使用实际参数 :调用中出现的参数。它们仅用于确定适用的过载。一旦编译器确定哪些重载适用且可访问,它将仅使用形式参数选择最具体的重载:声明中出现的参数。
我将结束 Effective Java 2nd Edition 的引用,第41项:明智地使用重载:
确定选择哪个重载的规则非常复杂。他们在语言规范中占用三十三个页面,很少有程序员理解他们的所有细微之处。
毋庸置疑,本书也是强烈推荐的。
答案 1 :(得分:5)
没有魔力。您必须将null参数转换为ObjectOne或ObjectTwo。
所以:
new TestClass((ObjectOne) null);
或
new TestClass((ObjectTwo) null);
答案 2 :(得分:1)
编译器将失败,因为您对构造函数进行了不明确的调用。 C#编译器具有相同的行为。
为了使这两种语言都可以工作,你必须将null
强制转换为两种类型中的任何一种,以消除对构造函数的调用。
答案 3 :(得分:1)
显然不会编译。
答案 4 :(得分:1)
编译错误:构造函数TestClass不明确
答案 5 :(得分:0)
我不知道演员是否会奏效。你可以试试这个:
ObjectOne o1 = null;
new TestClass(o1);