模糊的varargs方法

Dmy*_*tro 4 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

有人能告诉我为什么这些方法含糊不清吗?先感谢您.

Era*_*ran 7

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

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

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

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

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

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

JLS 15.12.2.5.选择最具体的方法说:

如果多个成员方法都可访问并适用于方法调用,则必须选择一个为运行时方法调度提供描述符.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更具特异性.另外,如果m2具有k + 1个参数,则m1的第k + 1个可变参数类型是第m + 1个可变参数类型m2的子类型.

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

由于int不是子类型IntegerInteger不是子类型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");
}
Run Code Online (Sandbox Code Playgroud)

由于Integer是子类型Object,因此在您调用时将选择第二种方法method(1).


Pau*_*ton 6

考虑方法签名

public static void foo(int a)
Run Code Online (Sandbox Code Playgroud)

public static void foo(Integer a)
Run Code Online (Sandbox Code Playgroud)

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

但是,method(1)方法签名的解析允许装箱和取消装箱,因为两种方法都需要变量arity调用.由于允许拳击,两个签名都适用.通常,当两个过载应用时,选择最具体的过载.但是,你的签名都int没有比另一个更具体(因为它们都不Integer是另一个的子类型).因此,电话method(1)不明确.

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