Varargs Java Ambiguous Call

我对Java的varargs方法有点困惑:

public static int sum(int ...a) {
    return 0;
}

public static double sum(double ...a) {
    return 0.0;
}

当我尝试在不传递任何参数的情况下调用时,则调用了方法的版本。我不明白为什么;通常编译器必须引发错误。sum()int

相比之下,当我尝试在没有任何参数的情况下调用时,以下代码段会生成编译器错误:sum

public static int sum(int ...a) {
    return 0;
}

public static boolean sum(boolean ...a) {
    return true;
}

答案 1

这里适用的一般规则是这样的:如果一个方法签名严格地比另一个方法签名更具体,那么Java会选择它而不会出现错误。

从直觉上讲,如果可以完全删除方法签名,则方法签名会更具体,而另一个不太具体的方法签名将适用于每个现有调用。

当在签名和 之间进行选择时,签名更具体,因为该方法的任何调用也可以通过应用加宽转换来传递。对于不能进行类似转换的方法,这同样不成立。sum(int... args)sum(double... args)sum(int... args)sum(double... args)sum(boolean... args)

Java 语言规范,SE 8 版本:

15.12. 方法调用表达式

15.12.2.5. 选择最具体的方法

Java 编程语言使用选择最具体方法的规则。

...

一个适用的方法 m1 比另一个适用的方法 m2 更具体,用于使用参数表达式 e1, ..., ek 的调用(如果满足以下任一条件):

...

  • m2 不是泛型的,m1 和 m2 通过严格或松散调用适用,其中 m1 具有形式参数类型 S1, ...,Sn 和 m2 具有形式参数类型 T1, ..., Tn,对于所有 i 的参数 ei,类型 Si 比 Ti 更具体(1 ≤ i ≤ n, n = k)。

...

对于任何表达式,如果 S <:T (§4.10),则类型 S 比类型 T 更具体。


4.10. 子类型

4.10.1. 基元类型之间的子类型

双>1浮子

浮>1长

长 >1 整型


答案 2

本答案中所述,在选择要使用的重载方法时,应遵循一些规则。

引用:

  1. 基元加宽使用尽可能小的方法参数
  2. 包装器类型无法加宽为其他包装类型
  3. 您可以从 int 到整数,然后加宽到对象,但不能加宽到长整型
  4. 加宽打败拳击,拳击打败Var-args。
  5. 您可以装箱,然后加宽(整数可以通过整数成为对象)
  6. 你不能加宽,然后盒子(一个int不能变成长)
  7. 不能将 var-args 与加装箱相结合。

(让我们像这样重新定义规则1:“原始加宽尽可能使用最具体的方法参数。

因此,考虑到这些规则,我们可以了解这里发生了什么:

根据规则一,基元加宽尽可能使用最具体的方法参数。由于 a 由非十进制数(例如 )表示,而 a 由精度比 a 高 32 字节的十进制数表示(例如),我们可以说 s “小于”或“小于”s,并且根据该逻辑,s 可以“提升”为 s,s 可以“降级”为 s。int1doublefloat1.0intdoubleintdoubledoubleint

简单地说,一个可以拓宽到另一个基元的基元(例如 -> ->) 比另一个更具体。例如,a 比 a 更具体,因为可以提升为 。intfloatdoubleintdouble11.0

当您没有向这些同名的重载 vararg 方法传递任何参数时,由于返回实际上是相同的(分别为 0 和 0.0),编译器将选择使用采用 vararg 类型的方法,因为它更具体int

因此,当您引入这些分别采用 s 和 s(不能相互扩展的类型)的相同方法时,编译器现在无法选择要使用的方法,因为 s 不能像 s、s 和 s 那样“提升”或“降级”。因此,它将引发编译错误。intbooleanintintfloatdouble

我希望这有助于您了解正在发生的事情。