Varargs在Java中的方法重载

时间:2012-10-14 06:39:10

标签: java overloading variadic-functions java-6

以下代码无法编译。

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

发出编译时错误。

  

对测试的引用是模棱两可的,两者都是方法测试(int ...)   varargspkg.Main和方法测试(浮动...)在varargspkg.Main匹配

这似乎很明显,因为方法调用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方法,或者它   程序员很难找出哪些重载   调用。

link中的最后一句话。然而,这是为了更好地理解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");
    }
}

修改

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

enter image description here

我正在使用JDK 6.

4 个答案:

答案 0 :(得分:10)

你可以WidenBox,但你不能同时做到这两件事,除非你是boxing and wideningObject(一个int到整数(拳击)然后整数到对象(扩展)是合法的,因为每个类都是Object的子类,因此Integer可以传递给Object参数)

同样,intNumber也是合法的(int - &gt; Integer - &gt; 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. 加宽节拍拳击,拳击比赛。
  5. 您可以选择Box,然后加宽(int Object可以通过Integer成为int
  6. 你不能加宽然后加框(一个Long不能成为Widened
  7. 你无法将var-args与扩展或装箱结合起来
  8. 因此,基于上述规则: -

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

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

    但在第一种情况下,你有public static void test(int...i) public static void test(float...f) 原始类型的方法: -

    test(1, 2)

    然后rule 1可以调用这两种方法(因为它们都不适合var-args申请): -

    • 在第一种情况下,它将是public static void test(int i) public static void test(float f)
    • 在第二种情况下,它将是加宽,然后是Var-args(允许)

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

    test(1)

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

    有关详细信息,请参阅JLS - Method Invocation Conversion

答案 1 :(得分:2)

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

另一方面,如果你写1F,它就是float的代表,可以自动装箱到Float(并且,同样的精神,绝不会自动装箱到Integer或其他任何内容。

答案 2 :(得分:2)

在Java 6中,问题出在您的泛型instantiation之前,然后才能找到可以调用的方法。

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 中,因为它可以更好地处理数据类型检查和升级。

答案 3 :(得分:2)

  

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

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

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

根据讨论,在过去bug with varargs and overloading?中已经在SO中讨论了这个问题,并且实际上是a bug