使用null的方法重载选择
鉴于此代码:
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
问题在于第一行,我希望它与其他两行相同。 此外,我发誓,直到最近,编译器才会给我一个明确的空调用的模糊调用警告。 但是,使用Java 5和6进行编译和测试会产生相同的结果。
这对我来说是一个重要的问题,因为我有很多代码使用这种模式使用不同类型的重载“默认”参数来选择返回类型并推断所需的转换/解析。 谁能解释一下这里发生了什么?
Java始终以相同的方式工作:始终选择“最具体”的适用重载。 由于String
是Object
的子类,因此它“更具体”,并且选择了String
重载。 如果重载是,例如String
和Integer
,并且您尝试传递null
,那么您确实会遇到编译时模糊错误,因为它们都处于同一inheritance层次结构的同一级别。
请记住,文字null
的类型为“特殊null类型”,而不是Object
类型
一个常见的混淆是文字null
是Object
类型,因此引导人们相信最接近的匹配签名是target(Object val, String chk)
。
文字null
实际上是“[特殊空类型]”类型( Java语言规范(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 。