Java 中方法重载中的 Varargs

以下代码无法编译。

package varargspkg;

public class Main {

    public static void test(int... i) {
        for (int t = 0; t < i.length; t++) {
            System.out.println(i[t]);
        }

        System.out.println("int");
    }

    public static void test(float... f) {
        for (int t = 0; t < f.length; t++) {
            System.out.println(f[t]);
        }

        System.out.println("float");
    }

    public static void main(String[] args) {
        test(1, 2);  //Compilation error here quoted as follows.
    }
}

发出编译时错误。

对 test 的引用是模棱两可的,两种方法 test(int...) 在 varargspkg 中。在varargspkg中的主要和方法测试(浮法...主场比赛

这似乎是显而易见的,因为方法调用中的参数值可以提升到以及test(1, 2);intfloat

如果任何人或两个参数都以 或 为后缀,则进行编译。Ff


但是,如果我们使用各自的包装器类型表示方法签名中的接收参数,如下所示

public static void test(Integer... i) {
    System.out.println("Integer" + Arrays.asList(i));
}

public static void test(Float... f) {
    System.out.println("Float" + Arrays.asList(f));
}

则对该方法的调用不会发出任何编译错误。在这种情况下要调用的方法是接受一个 varargs 参数(前面代码段中的第一个参数)的方法。test(1, 2);Integer

为什么在这种情况下没有报告第一种情况下的错误?此处似乎都应用了自动装箱和自动类型升级。是否首先应用自动装箱以解决错误?

甲骨文文档说,

一般来说,你不应该重载varargs方法,否则程序员将很难弄清楚调用哪个重载。

链接中的最后一句。然而,这是为了更好地理解varargs。

另外,添加下面的代码编译就好了。

public class OverLoading {

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

    public static void load(int i) {
        System.out.println("int");
    }

    public static void load(float i) {
        System.out.println("float");
    }
}

编辑:

以下是指示编译错误的快照。我创建了一个新的应用程序,因此包名称是不同的。

enter image description here

我使用的是 JDK 6。


答案 1

你可以要么做,要么但你不能同时做这两件事,除非你是(整数到整数(Boxing)然后整数到对象(加宽)是合法的,因为每个类都是 的子类,所以可以传递给参数)WidenBoxboxing and wideningObjectObjectIntegerObject

类似地,to也是合法的(int -> Integer -> Number),因为Number是它的超类。intNumberInteger

让我们在您的示例中看到这一点:-

public static void test(Integer...i)

public static void test(Float...f)

在选择要选择的重载方法时,当合并装箱、加宽和 Var-args 时,将遵循一些规则: -

  1. 基元加宽使用方法参数可能smallest
  2. 包装器类型无法加宽为其他包装类型
  3. 您可以从 int 到整数,然后加宽到对象,但不能加宽到长整型
  4. 加宽打败拳击,拳击打败Var-args。
  5. 你可以 Box 然后 Widen (An 可以成为intObjectInteger)
  6. 你不能加宽,然后盒子(An不能成为intLong)
  7. 不能将 var-args 与加宽或装箱相结合

因此,基于上述给定的规则:-

当您将两个整数传递给上述函数时,

  • 根据规则3,它必须首先是,然后才适合,根据规则5(你不能加宽然后框)这是非法的。WidenedBoxedLong
  • 因此,它被盒装以存储在var-args中。Integer

但是在第一种情况下,您有基元类型的方法:-var-args

public static void test(int...i)
public static void test(float...f)

然后可以调用这两种方法(因为它们都不适合应用): -test(1, 2)rule 1

  • 在第一种情况下,它将是var-args
  • 在第二种情况下,它将是加宽,然后是Var-args(这是允许的)

现在,当你有一个int和一个flost的方法时: -

public static void test(int i)
public static void test(float f)

然后使用 调用 时,遵循规则 1,并选择尽可能小的加宽(即根本不需要加宽的地方)。因此将调用第一种方法。test(1)int

有关更多信息,您可以参考 JLS - 方法调用转换


答案 2

在 Java 中,是 表示 .它可以自动装箱到 的实例或提升到 ,这就解释了为什么编译器不能决定它应该调用的方法。但它永远不会自动装箱到或(或任何其他类型)。1intIntegerfloatLongFloat

另一方面,如果你写,它是 a 的表示,可以自动装箱到 a(并且,本着同样的精神,永远不会自动装箱到一个或其他任何东西)。1FfloatFloatInteger