声明: 本文转自网络,转载来源是http://blog.csdn.net/sxzlc/article/details/6124099
public class TestNull {
public void show(String a){
System.out.println("String");
}
public void show(Object o){
System.out.println("Object");
}
public static void main(String args[]){
TestMain t = new TestMain();
t.show(null);
}
}
运行结果是:
String
解释(主要是重载函数调用时精确性的问题):
public class Confusing {
private Confusing(Object o) {
System.out.println("Object");
}
private Confusing(double[] dArray) {
System.out.println("double array");
}
public static void main(String[] args) {
new Confusing(null);
}
}
传递给构造器的参数是一个空的对象引用,因此,初看起来,该程序好像应该调用参数类型为Object的重载版本,并且将打印出Object。另一方面,数组 也是引用类型,因此null也可以应用于类型为double[ ]的重载版本。你由此可能会得出结论:
“这个调用是模棱两可的,该程序应该不能编译。”
如果你试着去运行该程序,就会发现这些直观感觉都是不对的:该程序打印 的是
double array
这种行为可能显得有悖常理,但是有一个很好的理由可以解释它。
Java的重载解析过程是以两阶段运行的:
第一阶段 选取所有可获得并且可应用的方法或构造器;
第二阶段在第一阶段选取的方法或构造器中选取最精确的一个。
如果一个方法或构造器可以接受传递给另一个方法或构 造器的任何参数,那么我们就说第一个方法比第二个方法缺乏精确性[JLS 15.12.2.5]。
在我们的程序中,两个构造器都是可获得 并且可应用的。构造器 Confusing(Object)可以接受任何传递给 Confusing(double[ ])的参数,因此 Confusing(Object)相对缺乏精确性。(每一个 double数组都是一个 Object,但是每一个 Object并不一定是 一个 double数组。)因此,最精确的构造器就是 Confusing(double[ ]),这也就解释了为什么程序会产生这样的输出。如果你传递的是一个double[ ]类型的值,那么这种行为是有意义的;但是如果你传递的是null,这种行为就有违直觉了。
理解本谜题的关键在于在测试哪一个方法或构造器最精确时,这些测试没有使用实际的参数:即出现在调用中的参数。这些参数只是被用来确定哪一个重载版本是可应用的。一旦编译器确定了哪些重载版本是可获得且可应用的,它就会选择最精确的一个重载版本,而此时使用的仅仅是形式参数:即出现在声明中的参数。
要想用一个null参数来调用Confusing(Object)构造器,你需要这样写代码:
new Confusing((Object)null)
这可以确保只有Confusing(Object)是可应用的。更一般地讲,要想强制要求编译器选择一 个精确的重载版本,需要将实际的参数转型为形式参数所声明的类型。
以这种方式来在多个重载版本中进行选择是相当令人不快的。在你的API 中,应该确保不会让客户端走这种极端。理想状态下,你应该避免使用重载:为不同的方法取不同的名称。当然,有时候这无法实现,例如,构造器就没有名称,因 而也就无法被赋予不同的名称。然而,你可以通过将构造器设置为私有的并提供公有的静态工厂,以此来缓解这个问题[EJ Item 1]。如果构造器有许多参数,你可以用Builder模式[Gamma95]来减少对重载版本的需求量。