Given this code:
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");
}
}
the output is (unexpectedly) as follows:
String[null] :: Should be Object
Object[null] :: Should be Object
Object[null] :: Should be Object
The problem is with the first line, which I expect to be the same as the other two. Furthermore, I would swear that until recently the compiler would give me an ambiguous invocation warning for the plain null
call. However compiling and testing with Java 5 and 6 yields the same results.
This is a significant issue for me since I have a lot of code which uses this pattern of using an overloaded "default" parameter of different types to select a return type and infer required conversion/parsing. Can anyone explain what is going on here?
Remember that the literal
null
is of type "special null type", not of typeObject
A common confusion is that the literal
null
is of typeObject
, thus leading people to believe the closest matched signature istarget(Object val, String chk)
.The literal
null
is actually of type "[special null type]" (Java Language Spec (JLS) 4). If it were possibly to define such a method, the closest match would betarget([special null type] val, String chk)
.However, since there isn't such a method (you couldn't create one), the compiler looks for the closest match through subtyping (JLS 15.12.2.2). The direct supertype of the [special null type] are all reference types (JLS 4.10.2) (e.g. String) and Object is a supertype of String.
Perhaps a more intuitive way to look at it is via the JLS's intuitive definition for the "most specific method" (JLS 15.12.2.5):
Of the two methods that the call
target(null ,"Object")
matches, any call tocould be handled by
so intuitively
void target(String val, String chk)
is the "most specific" that could be called without a type error.See the JLS 15.12.2.5 for how the "most specific" is formally defined.
Java has always worked the same way: the "most specific" applicable overload is always chosen. Since
String
is a subclass ofObject
, it is "more specific", and theString
overload is chosen. If the overloads were for, sayString
andInteger
, and you tried to passnull
, then you would indeed get a compile-time ambiguity error, since they are both at the same level of the same inheritance hierarchy.