我有addValues
的两个版本,一个带有vararg参数。
double addValues(double ... values) {
double result = 0d;
for (double value : values)
result += value;
return result;
}
double addValues(double v1, double v2) {
return v1 + v2;
}
当我调用addValues(2, 3)
看起来不明确时,为什么Java选择addValues(double v1, double v2)
版本来运行代码? Java如何确定哪个版本“更接近”调用?
感谢。
答案 0 :(得分:3)
This answer给出了Java语言规范的相关部分。然而,它是如此复杂,需要一些例子来解释。
如果可能,编译器将始终选择不需要“变量arity调用”或自动装箱或自动取消装箱的方法。变量arity调用是指通过传递最后一个参数(而不是数组)的参数列表来调用varargs方法。
例如,假设您有一个带签名的方法
void foo(int... arr)
这是一个可变的arity调用...
foo(1, 2, 3);
......但事实并非如此。
foo(new int[] {1, 2, 3});
所以在你的情况下,addValues(2, 3)
使用第二个版本,因为这不是一个可变的arity调用。
不能说编译器总是支持不涉及varargs的方法而不是涉及varargs的方法,如本示例所示:
public static void bar(int... a) {
System.out.println("Varargs");
}
public static void bar(Object a) {
System.out.println("Object");
}
public static void main(String[] args) {
bar(new int[] {1, 2, 3}); // Prints Varargs
}
在这个例子中,两个选择都不是变量arity调用,但是第一个版本被调用,因为它更具体。
这些规则可以更改非varargs签名
baz(int[] arr)
到varargs one
baz(int... arr)
不改变任何现有程序的行为。
答案 1 :(得分:2)
编译器通常会在varargs重载时支持非varargs重载。当在Java 5中添加varargs时,他们希望添加varargs重载,同时仍然保持向后兼容,这意味着必须调用非varargs重载的先前代码仍将调用该重载而不是varargs重载。
中对此进行了解释该过程的其余部分分为三个阶段,以确保与Java SE 5.0之前的Java编程语言版本兼容。阶段是:
- 第一阶段(§15.12.2.2)执行重载解析而不允许装箱或拆箱转换,或使用变量arity方法调用。如果在此阶段没有找到适用的方法,则处理继续到第二阶段。
醇>这保证了在Java SE 5.0之前在Java编程语言中有效的任何调用都不会因为引入变量arity方法,隐式装箱和/或取消装箱而被认为是不明确的。但是,变量arity方法(第8.4.1节)的声明可以更改为给定方法方法调用表达式选择的方法,因为变量arity方法在第一阶段被视为固定arity方法。例如,在已声明m(Object)的类中声明m(Object ...)会导致不再为某些调用表达式(例如m(null))选择m(Object),因为m(Object [] )更具体。
- 第二阶段(§15.12.2.3)执行重载解析,同时允许装箱和拆箱,但仍然排除使用变量arity方法调用。如果在此阶段没有找到适用的方法,则处理继续到第三阶段。
醇>这确保了如果通过固定的arity方法调用适用,则永远不会通过变量arity方法调用选择方法。
- 第三阶段(§15.12.2.4)允许重载与可变arity方法,装箱和拆箱。
醇>
(大胆强调我的)