使用Vararg重载Java方法

时间:2016-03-29 21:45:16

标签: java

我有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如何确定哪个版本“更接近”调用?

感谢。

2 个答案:

答案 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重载。

JLS, Section 15.12.2

中对此进行了解释
  

该过程的其余部分分为三个阶段,以确保与Java SE 5.0之前的Java编程语言版本兼容。阶段是:

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

    这保证了在Java SE 5.0之前在Java编程语言中有效的任何调用都不会因为引入变量arity方法,隐式装箱和/或取消装箱而被认为是不明确的。但是,变量arity方法(第8.4.1节)的声明可以更改为给定方法方法调用表达式选择的方法,因为变量arity方法在第一阶段被视为固定arity方法。例如,在已声明m(Object)的类中声明m(Object ...)会导致不再为某些调用表达式(例如m(null))选择m(Object),因为m(Object [] )更具体。

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

      这确保了如果通过固定的arity方法调用适用,则永远不会通过变量arity方法调用选择方法。

           
          
      1. 第三阶段(§15.12.2.4)允许重载与可变arity方法,装箱和拆箱。
      2.   

(大胆强调我的)