以下代码无法编译。
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
如果其中任何一个或两个参数都以F
或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);
的调用不会发出任何编译错误。在这种情况下要调用的方法是接受一个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。我创建了一个新的应用程序,因此包名称不同。
我正在使用JDK 6.
答案 0 :(得分:10)
你可以Widen
或Box
,但你不能同时做到这两件事,除非你是boxing and widening
到Object
(一个int到整数(拳击)然后整数到对象(扩展)是合法的,因为每个类都是Object
的子类,因此Integer
可以传递给Object
参数)
同样,int
到Number
也是合法的(int - &gt; Integer - &gt; Number)
由于Number是Integer
的超级类,因此是可能的。
让我们在您的示例中看到这一点: -
public static void test(Integer...i)
public static void test(Float...f)
当选择要选择哪种重载方法时,当组合Boxing,Widening和Var-args时,会遵循一些规则: -
smallest
方法参数int
Object
可以通过Integer
成为int
Long
不能成为Widened
)因此,基于上述规则: -
将两个整数传递给上述函数时,
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)
现在,当你有一个只有一个int和一个flost的方法时: -
test(1)
然后在使用int
进行调用时,遵循规则1,并且选择最小可能的加宽(即,{{1}},其中根本不需要加宽)。因此将调用第一个方法。
有关详细信息,请参阅JLS - Method Invocation Conversion
答案 1 :(得分:2)
在Java中,1
代表int
。它可以自动装箱到Integer
的实例,也可以提升为float
,这就解释了为什么编译器无法决定它应该调用的方法。但它永远不会自动装箱到Long
或Float
(或任何其他类型)。
另一方面,如果你写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.
另一方面,当您使用int
或float
时,没有变量类型实例化。当您使用1
时,它会首先尝试使用int
作为参数查找方法,否则,它会提升类型并使用float标识方法。如果两种方法都可用,则首先使用int
的方法。
模糊问题不会出现在Java 7 中,因为它可以更好地处理数据类型检查和升级。
答案 3 :(得分:2)
为什么在这种情况下会出现第一种情况中未报告的错误?这里似乎都应用了自动装箱和自动类型提升。是否首先应用了自动装箱错误?
只是一个意见 - 在varargs的情况下,JVM实际上必须创建一个参数数组。在Integer和Float的情况下,很明显它应该创建什么类型的数组。因此,它可能是没有歧义错误的原因。
但是,它仍然有点令人困惑,为什么它不能创建一个整数数组,默认情况下1,3是整数。
根据讨论,在过去bug with varargs and overloading?中已经在SO中讨论了这个问题,并且实际上是a bug。