Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call seems ambiguous, but runs perfectly with unexpected output

Tags:

java

null

Please see this Java Class

class Demo
{
    public static void a(String s)
    {
        System.out.println("string called");
    }
    public static void a(Object a)
    {
        System.out.println("Object called");
    }
    public static void main(String...asrgs)
    {
        a(null);
    }
}

The output of this code is "string called" but I am not able to understand that how compiler is able to resolve between Object and String.

Moreover, examine this code fragment

class Demo
{
    public static void a(String s)
    {
        System.out.println("string called");
    }
    public static void a(Integer n)
    {
        System.out.println("number called");
    }
    public static void a(Object a)
    {
        System.out.println("Object called");
    }
    public static void main(String...asrgs)
    {
        a(null);
    }
}

Here we get a compile time error related to ambiguous call (which is quite obvious). Any good explanations for this ?

like image 567
Gagan93 Avatar asked Oct 03 '14 07:10

Gagan93


1 Answers

The answer lies in §15.12.2 of the JLS:

The second step searches the type determined in the previous step for member methods. This step uses the name of the method and the argument expressions to locate methods that are both accessible and applicable, that is, declarations that can be correctly invoked on the given arguments.

There may be more than one such method, in which case the most specific one is chosen. The descriptor (signature plus return type) of the most specific method is the one used at run time to perform the method dispatch.

(my emphasis)

...and §15.12.2.5, which the section above refers to, which has the full details of specificity rules, but also this handy summary:

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 error.

In your first example, a(String) is more specific than a(Object), so the compiler knows which one to use and is happy. In your second example, both a(String) and a(Integer) are more specific than a(Object), but either is applicable to null and they're in separate lineages (String is String > Object, Integer is Integer > Number > Object), creating the ambiguity the compiler complains about.

If they were in the same lineage, there'd be no ambiguity, because there'd be a single applicable most specific option. For example:

class Base {
}
class Child extends Base {
}
class GrandChild extends Child {
}
public class Example {

    public static final void main(String[] args) {
        a(null);
    }

    public static void a(Base b) {
        System.out.println("Base");
    }

    public static void a(Child b) {
        System.out.println("Child");
    }

    public static void a(GrandChild b) {
        System.out.println("GrandChild");
    }
}

That prints "GrandChild", because while both a(Child) and a(GrandChild) are more specific than a(Object), a(GrandChild) is more specific than a(Child).

like image 135
T.J. Crowder Avatar answered Sep 30 '22 03:09

T.J. Crowder