鉴于此代码:
class Overloading
extends Object
{
static public void target(Object val, String chk) { System.out.println("Object["+val+"] :: Should be "+chk); }
static public void target(String val, String chk) { System.out.println("String["+val+"] :: Should be "+chk); }
static public void main(String[] args) {
Object obj=null;
target(null ,"Object");
target((Object)null,"Object");
target(obj ,"Object");
}
}
输出(意外地)如下:
String[null] :: Should be Object
Object[null] :: Should be Object
Object[null] :: Should be Object
问题在于第一行,我希望它与其他两行相同。此外,我发誓,直到最近,编译器才会给我一个简单的null
调用的模糊调用警告。然而,使用Java 5和6进行编译和测试会产生相同的结果。
这对我来说是一个重要的问题,因为我有很多代码使用这种模式使用不同类型的重载“默认”参数来选择返回类型并推断所需的转换/解析。谁能解释一下这里发生了什么?
答案 0 :(得分:6)
Java始终以相同的方式工作:始终选择“最具体”的适用重载。由于String
是Object
的子类,因此它更“具体”,并且选择String
重载。如果重载是,例如String
和Integer
,并且您尝试传递null
,那么您确实会遇到编译时模糊错误,因为它们都处于同一级别相同的继承层次结构。
答案 1 :(得分:3)
请注意,文字null
的类型为“特殊空类型”,而不是类型Object
常见的混淆是文字null
的类型为Object
,因此会让人们相信最匹配的签名是target(Object val, String chk)
。
文字null
实际上是“[特殊空类型]”(Java Language Spec (JLS) 4)类型。如果可能定义这样的方法,则最接近的匹配将是target([special null type] val, String chk)
。
但是,由于没有这样的方法(您无法创建),编译器会通过子类型(JLS 15.12.2.2)查找最接近的匹配。 [特殊null类型]的直接超类型都是引用类型(JLS 4.10.2)(例如String),Object是String的超类型。
通过JLS对“最具体方法”(JLS 15.12.2.5)的直观定义,或许更直观的方式来看待它:
“非正式的直觉是一种方法更具体 如果可以传递第一个方法处理的任何调用,则为另一个 没有编译时类型错误的另一个。“
在呼叫target(null ,"Object")
匹配的两种方法中,对
void target(String val, String chk)
可以由
处理void target(Object val, String chk)
如此直观void target(String val, String chk)
是可以在没有类型错误的情况下调用的“最具体”。
请参阅JLS 15.12.2.5了解“最具体”的定义方式。