4

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?

Lawrence Dol
  • 63,018
  • 25
  • 139
  • 189

2 Answers2

6

Java has always worked the same way: the "most specific" applicable overload is always chosen. Since String is a subclass of Object, it is "more specific", and the String overload is chosen. If the overloads were for, say String and Integer, and you tried to pass null, then you would indeed get a compile-time ambiguity error, since they are both at the same level of the same inheritance hierarchy.

Ernest Friedman-Hill
  • 80,601
  • 10
  • 150
  • 186
  • Yes, I believe you are correct; in other code I also had a overrides for either Number or primitive arrays, both of which are sufficient to trigger an ambiguity warning. I have resolved my current problem by adding a Number overload to conflict with String... but now I am much less happy with this general pattern (although there's now too much code depending on it to be worth changing it all). – Lawrence Dol Aug 26 '11 at 01:15
  • @SoftwareMonkey, i m facing similar problem as you did. i solved by adding a line in the function: `static public void target(String val, String chk) { if (val == null) { target((Object) val, chk); } System.out.println("String["+val+"] :: Should be "+chk); }`. Do you think it works well? – midnite May 08 '13 at 09:43
  • @midnite: No, that doesn't do it. In my case I use the parameter type to select the function invoked, which in turn determines the *return* type. But the `null` case is special and I need the ambiguity warning to remind the programmer to supply a "typed" null (that is, a specifically typed value initialized to null instead of the keyword `null`) to select the intended return type. – Lawrence Dol May 10 '13 at 20:29
  • Firstly, my apology that my code above was buggy. It should be `if (val == null) { target((Object) val, chk); return; }`. And thanks @SoftwareMonkey. This is really worth-thinking in the design. My case is a bit different. i am using the same function name for functions of different purposes. One `x(String, Object)` which i want to match everything except Strings. Another `x(String, String)` is doing a different thing. i know using both `x` might be bad. But it makes the user codes shorter and simpler (and seem smarter). Any recommendation with this? – midnite May 11 '13 at 02:05
  • How conflicts are resolved for functions having primitive data type, For ex. func(int, long) and func (long,int) and called as func(4,4). – Hemant Patel Mar 03 '15 at 13:15
  • @hermant: the literal value `4` is an `int` unless specifically cast like `(long)4`. – Lawrence Dol Mar 03 '15 at 20:44
3

Remember that the literal null is of type "special null type", not of type Object

A common confusion is that the literal null is of type Object, thus leading people to believe the closest matched signature is target(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 be target([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):

"The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time type error."

Of the two methods that the call target(null ,"Object") matches, any call to

void target(String val, String chk)

could be handled by

void target(Object val, String chk)

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.

Bert F
  • 85,407
  • 12
  • 106
  • 123