调用似乎含糊不清,但与意外输出完美匹配

Gag*_*n93 5 java null

请参阅此Java类

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

此代码的输出是"字符串调用",但我无法理解编译器如何能够在ObjectString之间进行解析.

此外,检查此代码片段

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这里我们得到一个与模糊调用相关的编译时错误(很明显).对此有什么好的解释?

T.J*_*der 5

答案在于JLS的第15.12.2节:

第二步搜索上一步中为成员方法确定的类型.此步骤使用方法名称和参数表达式来查找可访问和适用的方法,即可以在给定参数上正确调用的声明.

可能存在多于一种这样的方法,在这种情况下,选择最具体的方法.最具体方法的描述符(签名加返回类型)是在运行时用于执行方法调度的方法.

(我的重点)

......和上面提到的§15.12.2.5,其中包含特殊性规则的全部细节,以及这个方便的总结:

非正式的直觉是,如果第一个方法处理的任何调用都可以传递给另一个没有编译时错误的调用,那么一个方法比另一个方法更具体.

在您的第一个示例中,a(String)更具体a(Object),因此编译器知道使用哪一个并且很高兴.在你的第二个例子中,两者都比a(String)并且a(Integer)更具体a(Object),但是null它们都适用于它们并且它们处于不同的谱系(StringString > Object,IntegerInteger > Number > Object),从而产生编译器抱怨的歧义.

如果他们处于相同的血统中,就没有歧义,因为有一个适用的最具体的选择.例如:

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

这版画"GrandChild",因为同时兼具a(Child)a(GrandChild)比更具体a(Object),a(GrandChild)更不是具体的a(Child).