varargs和重载的错误?

时间:2010-03-26 05:28:16

标签: java overloading variadic-functions

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

3 个答案:

答案 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)更具体,因为intdouble的子类型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);
      }
   }