So null is always casted to the "more specific class that can be matched". If there's more than 1 class not belonging to the same hierarchy then an "ambiguous reference" compiler error is notified. Is that so?
We are talking about "Method Overload Resolution" here.
1) Most important thing to remember is that "Method Overload Resolution" happens at compile time i.e. Compiler does it.
2) Compiler resolves the method to be called in three phases mentioned below:
a) Accessibility: Whether the methods in question are accessible at the first place or not?
In the example above, print(Integer i) is not accessible to MethodOverloadDemo at the first place(Its a PRIVATE thing!), hence it is out of consideration. Result: print(String) is being called
B) After accessibility, compiler chooses the applicable methods.
A method is applicable if:
It number of argument matches.
Method can accept the arguments.
Now both String and Integer references can refer to null (to nothing)
Integer i = null;
String s = null;
At this point Compiler has two methods, both accessible and applicable, so it performs the third step: It chooses the MOST SPECIFIC METHOD
What is MOST SPECIFIC METHOD: A method is less specific if it can take all arguments passed successfully to the other method.
Lets take the above mentioned example with this:
1) Can a reference of type Integer refer to String object? Answer is NO: One cannot say Integer i = "Himanshu";
2) Can a reference of type String refer to an Integer object? Answer is NO: One cannot say String s = 1;
Compiler has done with all three steps, still it cannot choose one method. So its an ambiguity.
Lets take other example:
Now lets apply all three steps and see what happens:
1) Both are accessible
2) Both are applicable i.e. It is legal to say Object o = null; and String s = null;
3) Now third (choose most specific)
Can we say:
String s = new Object(); NO we cannot do that, pretty obviously
Can we say
Object o = new String("Himanshu"); YES, again obviously
In other words, print(Object o) can take all the arguments that can be passed to print(String s), however reverse is not true.
Now we, just like the compiler, can say print(Object o) is less specific and print(String s) is most/more specific and hence it wins.
Note: Apply the same rule irrespective of the whether actual argument is null or some other value.