错误与可变参数和重载?

在Java可变参数实现中似乎存在一个错误。 当一个方法被重载时,Java不能区分适当的types和不同types的可变参数。

它给了我一个错误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"); } } 

该文档说:“一般来说,你不应该重载可变参数方法,否则程序员很难找出哪个重载被调用。

但是他们没有提到这个错误,也不是程序员发现它很困难,而是编译器。

想法?

编辑 – 编译器:Sun jdk 1.6.0 u18

在Sun论坛上有关于此的讨论 。

没有真正的解决scheme,只是辞职。

可口可乐(和自动装箱,这也导致难以遵循的行为,特别是结合可变参数)后来在Java的生活中已经被狂奔,这是一个领域。 所以它在规范中比在编译器中更像是一个bug。

至less,这是很好的(?)SCJP技巧的问题。

问题是它模棱两可的。

 doIt(1, 2); 

可以打电话给doIt(int ...)doIt(double ...) 。 在后一种情况下,整数文字将被提升为double值。

我敢肯定,Java规范说这是一个模棱两可的结构,编译器只是遵循spec规定的规则。 (我必须进一步研究这一点。)

编辑 – JLS的相关部分是“ 15.12.2.5select最具体的方法 ”,但它让我头痛。

我认为理由是void doIt(int[])void doIt(double[])没有更具体的关系(反之亦然void doIt(double[])因为int[]不是double[]的子types(反之亦然)。 由于这两个重载是同样具体的,所以这个呼叫是不明确的。

相比之下, void doItAgain(int)void doItAgain(double)更具体,因为int是根据JLS的doubletypes。 因此,打电话给doItAgain(42)并不含糊。

编辑2 – @finnw是对的,这是一个错误。 考虑15.12.2.5的这一部分(编辑删除不适用的情况):

一个名为m的variablesarity成员方法比另一个具有相同名称的variablesarity成员方法更具体 ,如果:

一个成员方法有n个参数,另一个成员方法有k个参数,其中n≥k。 第一个成员方法的参数types是T1,…。 。 。 ,Tn-1,Tn [],另一种方法的参数types为U1,…, 。 。 ,Uk-1,Uk []。 令Si = Ui,1 <= i <= k。 然后:

  • 对于从1到k-1的所有j,Tj <:Sj,
  • 对于从k到n的所有j,Tj <:Sk

把这个应用到n = k = 1的情况,我们看到doIt(int[])doIt(double[])更具体。


事实上,这里有一个错误报告 ,Sun承认这确实是一个错误,虽然他们已经把它定为“非常低” 。 这个bug现在在Java 7中被标记为Fixed(b123)。

有趣。 幸运的是,有几种不同的方法来避免这个问题:

您可以在方法签名中使用包装types:

  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); } } 

或者,您可以使用generics:

  public static <T> void doit(T... ts) { for(T currT : ts) { System.out.println(currT); } }