views:

115

answers:

3

There seems to be a bug in the Java varargs implementation. Java can't distinguish the appropriate type when a method is overloaded with different types of vararg parameters.

It gives me an error The method ... is ambiguous for the type ...

Consider the following code:

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

the docs say: "Generally speaking, you should not overload a varargs method, or it will be difficult for programmers to figure out which overloading gets called."

however they don't mention this error, and it's not the programmers that are finding it difficult, it's the compiler.

thoughts?

EDIT - Compiler: Sun jdk 1.6.0 u18

+4  A: 
Stephen C
by that reasoning, doIt(int) and doIt(double) should also be ambiguous (without the varargs).
Thilo
It is the interaction with the varargs that makes this ambiguous, IIRC. You actually have two levels of promotion going on, 1) promotion of a sequence of args to an array, and 2) promotion of integral literals to doubles.
Stephen C
I think this is a bug (i.e. the compiler behaviour is not consistent with the JLS.) If I understand that section of the JLS correctly then `doIt(int...)` should be strictly more specific than `doIt(double...)` because `int` is a proper subtype of `double`. It is true that `int[]` is not a subtype of `double[]`, but that is not one of the requirements so it should not affect the overload resolution.
finnw
+2  A: 

There is a discussion about this over at the Sun Forums.

No real resolution there, just resignation.

Varargs (and auto-boxing, which also leads to hard-to-follow behaviour, especially in combination with varargs) have been bolted on later in Java's life, and this is one area where it shows. So it is more a bug in the spec, than in the compiler.

At least, it makes for good(?) SCJP trick questions.

Thilo
+2  A: 

Interesting. Fortunately, there are a couple different ways to avoid this problem:

You can use the wrapper types instead in the method signatures:

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

Or, you can use generics:

   public static <T> void doit(T... ts) {
      for(T currT : ts) {
         System.out.println(currT);
      }
   }
Rob Heiser
You cannot use generics, because then you have only one method. Presumably, the code does something else for doubles than it does for ints.
Thilo
Good point. I'll stick with using wrapper types in the signature then ::)
Rob Heiser
Interesting hack. It works because auto-boxing and promotion are never both applied to the same argument, so by forcing auto-boxing you prevent int-to-double promotion and only one method signature matches. This will *not* work for example #3 (with the `float` values.)
finnw