使用变量参数重载的方法(varargs)

Viv*_*mar 15 java overloading variadic-functions

看到这段代码的输出我感到很惊讶:

public class File
{
    public static void main(String[] args)
    {
        movie();
    }

    static void movie(double... x)
    {
        System.out.println("No varargs");
    }

    static void movie(int... x)
    {
        System.out.println("One argument");
    }
}
Run Code Online (Sandbox Code Playgroud)

它输出,

One argument
Run Code Online (Sandbox Code Playgroud)

为什么会这样?

我认为这段代码不能编译,因为调用movie()模糊的,但运行正常并输出One argument.

如果我将代码修改为:

public class File
{
    public static void main(String[] args)
    {
        movie();
    }

    static void movie(boolean... x)  //Changed the parameter type to boolean from double
    {
        System.out.println("No varargs");
    }

    static void movie(int... x)
    {
        System.out.println("One argument");
    }
}
Run Code Online (Sandbox Code Playgroud)

有一条错误消息.

为什么第一个代码运行正常,但第二个代码出错?

Tun*_*aki 10

这种行为是由于这个事实intdoubleint和之间没有这样的比较时更具体boolean.

正如JLS 第15.12.2.5节(强调我的)所述:

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

  • ...
  • m2不是通用的,m1和m2适用于变量arity调用,并且m1的前k个变量arity参数类型是S1,...,Sk和m2的前k个变量arity参数类型是T1,.. .,TK,该型Si比的Ti更加具体为参数EI对于所有的i(1≤I≤K).另外,如果m2具有k + 1个参数,则m1的第k + 1个可变参数类型是第m + 1个可变参数类型m2的子类型.

什么更具体的,其实就是在后文中定义的子类型:

如果S <:T,则类型S对于任何表达式比类型T更具体.

这意味着,S是更具体的比TS是的子类型T.对于基本类型,这归结为以下属性:

  • double> float
  • 漂浮>长
  • long> int
  • int> char
  • int>简短
  • 短>字节

注意boolean不存在.

因此,

public static void main(String[] args) {
    movie();
}

static void movie(int... x) { }
static void movie(short... x) { }
static void movie(double... x) { }
static void movie(byte... x) { }
Run Code Online (Sandbox Code Playgroud)

编译movie(byte... x)并将被调用,因为它是最具体的.

然而,

public static void main(String[] args) {
    movie();
}

static void movie(int... x) { }
static void movie(boolean... x) { }
Run Code Online (Sandbox Code Playgroud)

不编译因为boolean无法比较int.


Cod*_*roc 7

非正式的直觉是,如果第一个方法处理的任何调用都可以传递给另一个没有编译时错误的调用,那么一个方法比另一个方法更具体 .

考虑movie(int...x)M1movie(double...x)M2.

方法M1 比M2 更具体,因为我们可以使用给予方法M1的相同输入调用方法M2而没有任何编译时错误.

因此,第一方法M1的调用由M2定义地处理.因为double可以int毫无问题地处理.

但是我们不能使用给定方法M2的相同输入来调用M1,这很容易理解.

我们来看下面的例子,

public class Test {

    public static void main(String[] args) {
        movie();
    }

    static void movie(int... x) {
        System.out.println("One argument");
    }

    static void movie(short... x) {
        System.out.println("Short argument");
    }
}
Run Code Online (Sandbox Code Playgroud)

OUTPUT

Short argument
Run Code Online (Sandbox Code Playgroud)

因为这里shortint方法调用更具体movie().


另一方面,对于boolean方法调用movie();提出了混淆,因为编译器无法决定调用哪个方法,因为在这种情况下没有更具体方法的这一点.