编译器错误:引用调用不明确

2022-09-01 19:20:55

案例1

static void call(Integer i) {
    System.out.println("hi" + i);
}

static void call(int i) {
    System.out.println("hello" + i);
}

public static void main(String... args) {
    call(10);
}

案例1的输出:你好10

案例 2

static void call(Integer... i) {
    System.out.println("hi" + i);
}

static void call(int... i) {
    System.out.println("hello" + i);
}

public static void main(String... args) {
    call(10);
}

显示编译错误 。但是,我无法理解。为什么?但是,当我注释掉中的任何方法时,它就可以正常工作。谁能帮我了解,这里发生了什么?reference to call ambiguouscall()Case 2


答案 1

在Java语言特定(JLS)中以非常正式的方式定义最具体的方法。我在下面提取了在尝试尽可能多地删除正式公式时适用的主要项目。

总之,适用于您的问题的主要项目是:

第三阶段 (§15.12.2.4) 允许将重载与可变 arity 方法、装箱和取消装箱相结合。

  • 那么JLS 15.12.2.4基本上决定了这两种方法都适用,因为10可以同时转换为一个或一个。目前为止,一切都好。该段的结论是:Integer...int...

最具体的方法(§15.12.2.5)是从适用的可变方法中选择的。

  • 这就把我们带到了JLS 15.12.2.5。本段给出了一个 arity 方法比另一个 arity 方法更具体的条件。在只有一个参数而没有泛型的用例中,它归结为:m(a...)m(b...)

m(a...)比 iif 更具体,其中 意味着 。m(b...)a <: b<:is a subtype of

碰巧不是 的子类型,也不是 的子类型。intIntegerIntegerint

因此,要使用JLS语言,这两种方法都具有最大的特异性(没有一种方法比另一种方法更具体)。在这种情况下,同一段的结论是:call

  • 如果所有最大特定方法都具有覆盖等效(§8.4.2)签名[...]=>不是您的情况,因为不涉及泛型,整数和整数是不同的参数
  • 否则,我们说方法调用是不明确的,并且发生编译时错误。

注意

例如,如果您替换为,您将拥有并且最具体的方法将是*。
同样,如果替换为 ,则该方法将是最具体的。Integer...long...int <: longcall(int...)int...Number...call(Integer...)

*在Java 7之前,JDK中实际上存在一个错误,在这种情况下会显示一个模棱两可的调用


答案 2

看起来它与错误#6886431有关,这似乎在OpenJDK 7中得到了修复。

下面是错误描述,

错误描述:

当调用具有以下重载签名的方法时,我预计会出现歧义错误(假设参数与两者都兼容):

int f(Object... args);
int f(int... args);

javac将第二个视为比第一个更具体。这种行为是合理的(我更喜欢它),但与JLS(15.12.2)不一致。