模糊的varargs方法

时间:2015-01-10 08:41:14

标签: java methods variadic-functions

这是一个无法编译的代码示例:

public class Test {
    public static void main(String[] args) {
        method(1);
    }

    public static void method(int... x) {
        System.out.println("varargs");
    }

    public static void method(Integer... x) {
        System.out.println("single");
    }
}

有人能告诉我这些方法含糊不清的原因吗?提前谢谢。

4 个答案:

答案 0 :(得分:7)

重载解析(JLS 15.2.2)中使用了3个阶段:

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

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

  4.   
  5. 第三阶段(§15.12.2.4)允许重载与变量arity方法,装箱和拆箱相结合。

  6.   

在您的示例中,两种方法都是可变的arity方法,因此第三阶段适用。

现在,由于我们有两种方法可供选择,我们会寻找更具体的方法。

JLS 15.12.2.5. Choosing the Most Specific Method说:

  

如果多个成员方法都可访问并适用于方法调用,则必须选择一个为运行时方法调度提供描述符。 Java编程语言使用选择最具体方法的规则。

     

...

     

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

     

...

     

m2不是通用的,m1和m2适用于变量arity调用,并且m1的前k个变量arity参数类型是S1,...,Sk和m2的前k个变量arity参数类型是T1, ...,Tk,对于所有i(1≤i≤k),类型Si对于参数ei比Ti更具特异性。另外,如果m2具有k + 1个参数,则m1的第k + 1个变量参数类型是第k + 1个可变参数类型m2的子类型。

在您的情况下,您有两个非泛型方法,这些方法适用于变量arity调用(即两者都有varargs)。为了在调用method(1)时选择其中一种方法,其中一种方法必须比另一方更具体。在您的情况下,每个方法只有一个参数,并且其中一个参数比另一个更具体,那个参数的类型必须是另一个方法参数的子类型。

由于int不是Integer的子类型且Integer不是int的子类型,因此您的方法都不比其他方法更具体。因此The method method(int[]) is ambiguous for the type Test错误。

一个可行的例子:

public static void method(Object... x) {
    System.out.println("varargs");
}

public static void method(Integer... x) {
    System.out.println("single");
}

由于IntegerObject的子类型,因此在您致电method(1)时会选择第二种方法。

答案 1 :(得分:6)

考虑方法签名

public static void foo(int a)

public static void foo(Integer a)

在装箱和拆箱之前,电话foo(1)不会有歧义。为确保与早期版本的Java兼容,调用仍然是明确的。因此,重载决策的第一阶段不允许装箱,取消装箱或变量arity调用,这些都是同时引入的。变量arity调用是指通过为最后一个参数(而不是数组)传递一系列参数来调用varargs方法。

但是,方法签名的method(1)分辨率允许装箱和拆箱,因为两种方法都需要变量arity调用。由于允许拳击,两个签名都适用。通常,当两个过载应用时,选择最具体的过载。但是,您的签名都不比其他签名更具体(因为intInteger都不是另一个的子类型。因此,调用method(1)不明确。

您可以通过传递new int[]{1}来进行编译。

答案 2 :(得分:0)

因为 不明确。根据JLS,你可以做扩大,拳击或拳击 - 然后扩大。在您的示例中,有两个方法参数可以相互装箱/取消装箱。在编译时虽然因为varargs 而不可见,但在java中总是不是很清楚。

即使Sun建议开发人员不要重载varargs方法,编译器中也存在与之相关的错误(see here)。

答案 3 :(得分:-1)

int和Integer之间的区别在于Integer是一个对象类型。您可以在查找int类型的最大数量或与整数比较等情况下使用

整数对象已与比较方法:

等方法相关联
public static void method(int x, int y) {
    System.out.println(Integer.compare(x, y));
}

点击此处了解详情:http://docs.oracle.com/javase/7/docs/api/