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

时间:2014-10-03 07:22:16

标签: 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);
    }
}

此代码的输出为"字符串称为"但我无法理解编译器如何在对象字符串之间解析。

此外,请检查此代码片段

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);
    }
}

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

1 个答案:

答案 0 :(得分:5)

答案在于JLS的§15.12.2

  

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

     

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

(我的重点)

上面提到的

...和§15.12.2.5,它具有特异性规则的全部细节,但也有这个方便的总结:

  

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

在您的第一个示例中,a(String)a(Object)更具体,因此编译器知道使用哪一个并且很开心。在第二个示例中,a(String)a(Integer)都比a(Object)更具体,但两者都适用于null,并且它们位于不同的谱系中(StringString > ObjectIntegerInteger > 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");
    }
}

这会打印"GrandChild",因为虽然a(Child)a(GrandChild)都比a(Object)更具体,但a(GrandChild)a(Child)更具体。