23

以下代码无法编译。

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 的引用不明确,varargspkg.Main 中的方法 test(int...) 和 varargspkg.Main 中的方法 test(float...) 都匹配

这似乎很明显,因为方法调用中的参数值test(1, 2);可以提升intfloat

如果任何一个或两个参数都以For为后缀f,则编译。


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

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);不会发出任何编译错误。在这种情况下要调用的方法是接受一个Integervarargs 参数的方法(前面代码段中的第一个)。

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

Oracle 文档说,

一般来说,您不应该重载可变参数方法,否则程序员将很难弄清楚调用了哪个重载。

这个链接的最后一句话。然而,这是为了更好地理解可变参数。

还要添加下面的代码编译就好了。

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。

4

4 回答 4

10

你可以Widen或者Box但你不能两者都做,除非你boxing and wideningObject(An int to Integer(Boxing) 然后 Integer to Object(Widening) 是合法的,因为每个类都是 的子类Object,所以有可能Integer被传递给Object范围)

类似地,一个inttoNumber也是合法的 (int -> Integer -> Number) 因为 Number 是它的超类Integer是可能的。

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

public static void test(Integer...i)

public static void test(Float...f)

在选择要选择的重载方法时,在组合 Boxing、Widening 和 Var-args 时,需要遵循一些规则:-

  1. 基元扩展使用smallest方法参数可能
  2. Wrapper 类型不能扩展到另一个 Wrapper 类型
  3. 您可以从 int Box 到 Integer 并扩大到 Object 但不能扩大到 Long
  4. 扩大击败拳击,拳击击败 Var-args。
  5. 你可以 Box 然后加宽(一个int可以成为Objectvia Integer
  6. 你不能先加宽然后盒子(一个int不能成为Long
  7. 您不能将 var-args 与加宽或装箱结合使用

所以,根据上面给定的规则:-

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

  • 根据规则 3,它必须首先放入 a中,根据规则 5,这是非法的(您不能先加宽,然后再放入框)WidenedBoxedLong
  • 因此,存储在Integervar-args 中是盒装的。

但在第一种情况下,你有var-args原始类型的方法: -

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

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

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

现在,当您的方法恰好具有一个 int 和一个 flost 时:-

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

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

更多信息,您可以参考JLS - Method Invocation Conversion

于 2012-10-14T06:52:40.117 回答
2

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

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

于 2012-10-14T06:42:59.367 回答
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.

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

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

于 2012-10-14T06:48:40.507 回答
2

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

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

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

看起来这在过去的错误中已经在 SO 中讨论过可变参数和重载?根据讨论,实际上是一个错误。

于 2012-10-14T07:05:31.250 回答