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

2022-09-03 14:24:23

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

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");
    }
}

它输出,

One argument

为什么会这样?

我以为这段代码不会编译,因为对的调用是模棱两可的,但它运行良好并输出 。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");
    }
}

出现一条错误消息。

为什么第一个代码运行良好,但第二个代码给出错误?


答案 1

此行为是由于 与 之间没有此类比较时更具体。intdoubleintboolean

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

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

  • ...
  • m2 不是泛型的,m1 和 m2 通过变量 arity 调用适用,其中 m1 的第一个 k 变量 arity 参数类型是 S1, ...,Sk 和 m2 的第一个 k 个变量 arity 参数类型是 T1, ..., Tk,对于所有 i 的参数 ei,Si 类型比 Ti 更具体(1 ≤ i ≤ k)。此外,如果 m2 具有 k+1 个参数,则 m1 的第 k+1 个变量 arity 参数类型是 m2 的第 k+1'个变量 arity 参数类型的子类型。

更具体的实际含义稍后通过子类型定义

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

这意味着 它比 是 的子类型更具体。对于基元类型,这归结为以下属性STST

  • 双>浮子
  • 浮>长
  • 长>整型
  • 整型>字符
  • 整型 > 短
  • 短>字节

请注意,不存在。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) { }

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

然而

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

static void movie(int... x) { }
static void movie(boolean... x) { }

无法编译,因为无法与 进行比较。booleanint


答案 2

非正式的直觉是,如果第一个方法处理的任何调用都可以在没有编译时错误的情况下传递给另一个方法,则一种方法比另一个方法更具体。

考虑为 M1M2movie(int...x)movie(double...x)

方法 M1 比 M2 更具体,因为我们可以使用直接为方法 M1 提供的相同输入来调用方法 M2,而不会出现任何编译时错误。

因此,第一个方法 M1 上的调用明确由 M2 处理。因为可以毫无问题地处理。doubleint

但是我们不能使用给定方法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");
    }
}

输出

Short argument

因为这里比方法调用更具体。shortintmovie()


另一方面对于方法调用引起混淆,因为编译器无法决定调用哪个方法,因为在这种情况下没有更具体的方法booleanmovie();