重载 varargs 数组,选择方法

2022-09-04 22:43:28

我有两个重载方法:和foobar

//Object[]... vs Integer[]...
public static String foo(Object[]... args)  { return "Object[] args"; }
public static String foo(Integer[]... args) { return "Integer[] args";}

//Object... vs Integer[]...
public static String bar(Object... args) {return "Object args";}
public static String bar(Integer[]... args) {return "Integer[] args";}

现在,当我像这样使用它们时:

Integer[] i = { 5 };
System.out.println(foo(i));//Object[]... vs Integer[]...
System.out.println(bar(i));//Object... vs Integer[]...

我得到

Integer[] args
Object args

问题是:为什么我们有2个不同的输出?
可以隐式转换为 、 和 。Integer[]ObjectObject[]


答案 1

这基本上是编译器决定调用所有方法中最具体的方法。

当您致电时

System.out.println(foo(i));//Object[]... vs Integer[]...

它将调用foo(Integer[]... args)

因为在运行时,JVM 将调用委托给带有参数的方法,而不是将参数参数的参数委托给参数[][] 参数的方法。因为使用 Integer[][] 而不是 Object[][] 调用方法会更具体。Integer[][]


在后面的语句中,当您调用

System.out.println(bar(i));//Object... vs Integer[]...

它将转到bar(Object... args)

同样,通过使用varags,参数的类型将是Object[]而不是Object[][]。同样,编译器将调用最具体的方法,该方法将是具有.Object... args

如果通过删除 varags 来更改方法签名,如下所示:

   //Object... vs Integer[]...
    public static String bar(Object args) {

        return "Object args";
    }

    public static String bar(Integer[] args) {
        return "Integer[] args";
    }

然后你会注意到它会调用,因为它更特定于方法调用。bar(Integer[] args)

因此,为了更精确地按照数组类型中的JLS子类型,

  • 如果 S 和 T 都是引用类型,则 S[] > T[] iff S > T。
  • 对象>对象[]

这意味着将调用 Integer[] 对具有 Integer[][] 而不是 Object[][] 的方法进行调用。其中,作为对 Integer[] 的调用,将对 Object[] 而不是 Integer[][] 进行调用。

有关选择最具体的方法,请参阅此处


答案 2

在第一种情况下,args的类型实际上是 Integer[][],即你的数组被 varargs 装箱到另一个数组中。编译器选择 Integer[] 版本,因为它是最具体的类型。

在第二种情况下,args == i 并且是 Integer[]。在这种情况下,编译器必须在将其包装在新数组中以调用Integer[]...版本或只是将整数[]转换为对象[]。它选择了第二个,因为这是规则。

这个故事的寓意是:不要超载 varargs 方法——这令人困惑。