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. } } 

发出编译时错误。

参考测试是不明确的,varargspkg.Main中的方法测试(int …)和varargspkg中的方法测试(float …)匹配

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

如果任何一个或两个参数后缀为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)); } 

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

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

Oracle文档说,

一般来说,你不应该重载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"); } } 

编辑:

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

在此处输入图像描述

我正在使用JDK 6。

你可以boxing and wideningBox但你不能同时做到这Widen ,除非你boxing and wideningObject (一个int到整数(拳击)然后整数到对象(加宽)是合法的,因为每个类都是Object的子类,所以它可以将Integer传递给Object参数)

类似地,对Numberint也是合法的(int – > Integer – > Number)因为Number是Integer的超类,所以它是可能的。

我们在你的例子中看到这个: –

 public static void test(Integer...i) public static void test(Float...f) 

选择要选择哪种重载方法,组合Boxing,Widening和Var-args时,遵循一些规则: –

  1. 原始扩展使用可能的smallest方法参数
  2. 包装类型不能扩展到另一种包装类型
  3. 您可以从int到Integer并将其扩展为Object而不是Long
  4. 加宽节拍拳击,拳击节拍Var-args。
  5. 你可以Box然后加宽(一个int可以通过Integer成为Object
  6. 你不能加宽然后Box(一个int不能变Long
  7. 你不能将var-args与拓宽或装箱结合起来

因此,根据上述规则: –

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

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

但在第一种情况下,你有使用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) 

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

有关更多信息,请参阅JLS - Method Invocation Conversion

在Java中, 1表示如何表示int 。 它既可以自动装入Integer实例,也可以提升为float ,这就解释了为什么编译器无法决定它应该调用的方法。 但它永远不会被自动装箱为LongFloat (或任何其他类型)。

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

为什么在这种情况下错误与第一种情况没有报告? 这里似乎都应用了自动装箱和自动类型提升。 是否首先应用自动装箱错误?

只是一个意见 – 在varargs的情况下,JVM实际上必须创建一个参数数组。 在Integer和Float的情况下,很明显它应该创建什么类型的数组。 因此,它可能是没有歧义错误的原因。

但是,它有点令人困惑,为什么它不能创建一个整数数组,默认情况下1,3是整数。

在过去的varargs和重载错误中,这里已经讨论过这个了吗? 根据讨论,这实际上是一个错误

在Java 6中,问题出在您的genericsinstantiation之前,然后找到可以调用的方法。

 When you write 1,2 -> it can be be both int[] or float[] and hence the issue being complained. When you write 1,2F -> it can be be only float[] and hence the NO issue being complained. 

与其他两个选项相同,即

 When you write 1F,2 -> it can be be only float[] and hence the NO issue being complained. When you write 1F,2F -> it can be be only float[] and hence the NO issue being complained. 

另一方面,当您使用intfloat ,没有变量类型实例化。 当你使用1 ,它首先尝试使用int作为参数来查找方法,否则,它会提升类型并使用float标识方法。 如果两种方法都可用,则首先使用带有int方法。

Java 7中不会出现歧义问题,因为它可以更好地处理数据类型检查和升级。