Java varargs实现中似乎存在一个错误。当方法使用不同类型的vararg参数重载时,Java无法区分适当的类型。
它给我一个错误The method ... is ambiguous for the type ...
请考虑以下代码:
public class Test
{
public static void main(String[] args) throws Throwable
{
doit(new int[]{1, 2}); // <- no problem
doit(new double[]{1.2, 2.2}); // <- no problem
doit(1.2f, 2.2f); // <- no problem
doit(1.2d, 2.2d); // <- no problem
doit(1, 2); // <- The method doit(double[]) is ambiguous for the type Test
}
public static void doit(double... ds)
{
System.out.println("doubles");
}
public static void doit(int... is)
{
System.out.println("ints");
}
}
docs说:“一般来说,你不应该重载varargs方法,否则程序员很难弄清楚调用哪些重载。”
然而,他们并没有提到这个错误,并不是程序员发现它很困难,而是编译器。
想法?
编辑 - 编译:Sun jdk 1.6.0 u18
答案 0 :(得分:13)
问题在于 不明确。
doIt(1, 2);
可以是对doIt(int ...)
或doIt(double ...)
的来电。在后一种情况下,整数文字将被提升为double
值。
我很确定Java规范说这是一个模糊的构造,编译器只是遵循规范规定的规则。 (我必须进一步研究这一点。)
编辑 - JLS的相关部分是“15.12.2.5 Choosing the Most Specific Method”,但它让我头疼。
<击>
我认为推理的原因是void doIt(int[])
并不比void doIt(double[])
更具体(反之亦然),因为int[]
不是double[]
的子类型(反之亦然)。由于两个重载具有相同的特定性,因此调用不明确。
相比之下,void doItAgain(int)
比void doItAgain(double)
更具体,因为int
是double
的子类型JLS。因此,对doItAgain(42)
的调用并不含糊。
击>
编辑2 - @finnw是对的,这是一个错误。考虑15.12.2.5的这一部分(编辑以删除不适用的案例):
一个名为m的变量arity成员方法更具体而不是另一个同名的变量arity成员方法,如果:
一个成员方法有n个参数,另一个成员方法有k个参数,其中n≥k。第一个成员方法的参数类型是T1 ,. 。 。 ,Tn-1,Tn [],其他方法的参数类型是U1 ,. 。 。 ,Uk-1,英国[]。设Si = Ui,1 <= i <= k。然后:
- 对于从1到k-1的所有j,Tj&lt;:Sj,和
- 表示从k到n的所有j,Tj&lt;:Sk
将此应用于n = k = 1的情况,我们发现doIt(int[])
比doIt(double[])
更具体。
事实上,这个有一个bug report,并且Sun承认它确实是一个bug,尽管他们已经将它优先化为“非常低”。该错误现在在Java 7(b123)中标记为已修复。
答案 1 :(得分:7)
关于此问题的讨论超过at the Sun Forums。
没有真正的解决方案,只是辞职。
Varargs(以及自动拳击,这也导致难以理解的行为,特别是与varargs结合使用)已经在Java的生命中被拴住了,这是它所展示的一个领域。所以它在规范中比在编译器中更像是一个错误。
至少,它会带来好的(?)SCJP技巧问题。
答案 2 :(得分:4)
有趣。幸运的是,有几种不同的方法可以避免这个问题:
您可以在方法签名中使用包装类型:
public static void doit(Double... ds) {
for(Double currD : ds) {
System.out.println(currD);
}
}
public static void doit(Integer... is) {
for(Integer currI : is) {
System.out.println(currI);
}
}
或者,您可以使用泛型:
public static <T> void doit(T... ts) {
for(T currT : ts) {
System.out.println(currT);
}
}