为什么两个带有签名(基元,包装器)和(基元,基元)的方法会导致方法调用(包装器,基元)不明确?

2022-09-03 01:14:26

这只是一个练习,但我无法弄清楚歧义:

private static void flipFlop(String str, int i, Integer iRef) {
System.out.println(str + "ciao");
}

private static void flipFlop(String str, int i, int j) {
System.out.println(str + "hello");
}

public static void main(String[] args) {
flipFlop("hello", new Integer(4), 2004);
}

它说:

方法 flipFlop(String, int, Integer) 对于 Test 类型是不明确的

我本来会猜到第二个参数会被解开成int,所以第二种方法将是选择。flipFlop


答案 1

如果您喜欢引人入胜的阅读,以下是Java语言规范的相关部分,其中描述了如何解析方法。

但基本上你的第三个参数可以被解释为一个基元或一个自动装箱的包装器,编译器无法弄清楚你想要什么。这两种方法都是“最大特异性”,以使用JLS术语。


答案 2

好吧,我已经仔细研究了JLS,我相信这应该可以消除您可能遇到的任何剩余疑问。

这是最初的问题:

public class Main {
    private static void flipFlop(int i, Integer iRef) {
        System.out.println("Method 1");
    }

    private static void flipFlop(int i, int j) {
        System.out.println("Method 2");
    }

    public static void main(String[] args) {
        flipFlop(new Integer(4), 2004);
    }
}

正如在另一个答案中指出的那样:这失败了,因为编译器无法决定使用什么重载。

但是,您可能会认为这没有任何意义。在这种情况下,编译器可以决定他应该使用什么方法:

public class Main {
    private static void flipFlop(Integer y) {
        System.out.println("ciao");
    }

    private static void flipFlop(int j) {
        System.out.println("hello");
    }

    public static void main(String[] args) {
        flipFlop(new Integer(6));
        flipFlop(6);
    }
}

理性告诉我们,当你有值+和两个分别取+和+的方法,并且你知道并且是可互换的,那么这意味着后一种方法更具体。XYYXYYXY

JLS中描述了这两者之间的区别。我在下面提供了整个工作流程,但重要的是:

首先,编译器将查看具有相等签名的方法,同时禁止装箱/取消装箱。在我们的第二个示例中,这不会导致任何问题,但在第一个示例中,这不会返回可满足的方法,因为它们都不采用 a 作为第一个参数。Integer

当失败时,编译器将进入第二步,允许装箱/取消装箱。这应该可以解决我们对第一个参数遇到的问题,但现在会导致第二个参数的歧义,因为现在不确定您是使用 an 还是使用 .intInteger

这最终会导致不明确的方法调用。

15.12.2. 编译时步骤 2:确定方法签名

  1. 第一阶段 (§15.12.2.2) 执行重载解析,不允许装箱或取消装箱转换,也不允许使用可变 arity 方法调用。如果在此阶段找不到适用的方法,则处理将继续到第二阶段。

  2. 第二阶段 (§15.12.2.3) 在允许装箱和取消装箱的同时执行重载解析,但仍排除使用可变 arity 方法调用。如果在此阶段找不到适用的方法,则处理将继续到第三阶段。

如果在适用性测试的三个阶段之一中确定了几种适用的方法,则选择最具体的方法,如§15.12.2.5节所述。

15.12.2.5. 选择最具体的方法

如果一个方法可访问且适用,并且没有其他适用且可访问的严格更具体的方法,则该方法对于方法调用具有最大特异性。

可能没有一种方法是最具体的,因为有两种或两种以上方法具有最大的特异性。在这种情况下:

  • 如果所有最大特定方法都具有覆盖等效 (§8.4.2) 签名,则:(...一些规则来决定谁被选中...)

  • 否则,我们说方法调用是不明确的,并且发生编译时错误。


推荐