2

I want to create two functions, say

long min(long...);
int min(int...);

But when I try to invoke the second i.e min(1, 5) one I get ambiguous method call

Is there workaround except renaming?

RiaD
  • 46,822
  • 11
  • 79
  • 123

4 Answers4

7

It is a known bug

The behaviour you describe is a bug which has been fixed with Java 7. See details in the release notes, section called "Changes in Most Specific Varargs Method Selection".

The reason why it should compile

Variable arity comes last in determining the most specific method. The rules to determine which vararg method applies when there are several are defined in JLS 15.12.2.4 - here is an extract:

One variable arity member method named m is more specific than another variable arity member method of the same name if either:
- [...]
- One member method has k parameters and the other has n parameters, where n ≥ k, and:

  • The types of the parameters of the first method are U1, ..., Uk-1, Uk[].
  • The types of the parameters of the other method are T1, ..., Tn-1, Tn[].
  • For all j from 1 to n, Uj <: Tj

In your case, k = n, and U1[] = int[] and T1[] = long[] so the determination can be made if int <: long or the opposite.

In other words, the type taken into account is not int[] vs. long[] but int vs long. And it happens that int <: long so the int... method should be chosen and it should compile.

Conclusion:

The code should (and does) compile fine with Java 7 but would not compile with Java 5 or 6. The code below prints int with Java 7:

public class Test1 {
    public static void main(String[] args) {
        new Test1().m(1, 2);
    }
    int m(int... i) {
        System.out.println("int");
        return 0;
    }
    long m(long... i) {
        System.out.println("long");
        return 0;
    }
}
assylias
  • 321,522
  • 82
  • 660
  • 783
  • Umm.. Did you try to compile it? It does not compile, at least for me. – Rohit Jain Nov 23 '12 at 07:28
  • @RohitJain I understand what is going on. Updating my answer. – assylias Nov 23 '12 at 07:29
  • @RohitJain It is a bug in Java <6 – assylias Nov 23 '12 at 07:32
  • @assylias.. Well, in that case, it is not necessary that OP is using Java 7. Before Java 7 of course it does not work. So, I think my answer is valid, and does not needs a downvote. BTW, thanks for quoting that it works in Java 7. Haven't come through it till yet. :) – Rohit Jain Nov 23 '12 at 07:32
  • @RohitJain The release notes specifically state that it was a bug so although it did not compile with Java 5 or 6 your analysis of the reason is not correct if I may. – assylias Nov 23 '12 at 07:35
  • @RohitJain In particular, based on your answer, if you remove the `int...` method, it should still not compile, but it does with Java 6, because the `int`s are converted to `long`s. – assylias Nov 23 '12 at 07:36
  • 1
    @assylias.. Well, I haven't used Java 7 till now. So, of course I could not have known that bug fix before you told me. And as I knew that was the problem, so I didn't bother to look for any bug fixes in Java 7. – Rohit Jain Nov 23 '12 at 07:37
  • @assylias.. Why would it not compile when `int...` is removed? That is the whole point. Both the method were valid, and hence there was ambiguity. If one method is removed, it will compile fine. – Rohit Jain Nov 23 '12 at 07:38
  • @assylias.. Anyways, +1 to your answer for bringing it to notice. And yes, I have added a link to your answer on my answer, to make it complete. :) – Rohit Jain Nov 23 '12 at 07:40
1

The problem is when you invoke those method with an integer, none of the methods provide exact match for the argument, as you used var-args. Also, both the var-args can take int values separated by commas as argument. Hence the ambiguous method error.

When you call overloaded method, Compiler chooses the exact match first. Then the nearest type to cast.

Now, since var-args is just an array, compiler cannot decide which array to use while passing an integer value. As, neither arrays can be considered as exact match or nearest conversion. So, it takes both the method eligible for call.

There are lot more rules regarding the method invocation in case of Overloading. You can learn those from the Java Language Specification.


As in @assylias answer this problem has been fixed in Java 7. But it will not work in Java version < 7.

Community
  • 1
  • 1
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • +1 too. `long...` acts like `long[]` and `int[]` cannot be casted to `long[]` so it's not more specific – John Dvorak Nov 23 '12 at 07:03
  • @assylias.. If used with var-args, it won't compile. I think that is the problem OP is facing. He might be using var-args on int and lon. – Rohit Jain Nov 23 '12 at 07:21
  • @RohitJain See my answer. The comparison is not between `int[]` and `long[]`, it is between `int` and `long`. – assylias Nov 23 '12 at 07:26
  • @assylias.. No. The decision here is, whether to convert `int` to `int[]` or `long[]`. Your code does not compile. Please test it. – Rohit Jain Nov 23 '12 at 07:30
  • @JanDvorak This is not the case: `int[]` is not more specific than `long[]` but `int...` is more specific than `long...`. – assylias Nov 23 '12 at 07:44
1

Try this

public class Test {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        new Test().m(100L,512L);
        new Test().m(100,512);
    }

    int m(int... i) {
        System.out.println("int");
        return 0;
    }

    long m(long... i) {
        System.out.println("long");
        return 0;
    }
}
RiaD
  • 46,822
  • 11
  • 79
  • 123
Vipul Paralikar
  • 1,508
  • 10
  • 22
0

Those method signatures are unique, and are valid method overloads.

The problem must be in the method call argument. My guess is that you are calling the method with a literal, like:

min(100) 

And it doesnt like that because 100 is an integer literal, but it gets automatically converted to a long.

disambiguating the method calls by using L at the end of long literals should fix the problem:

min(100L)

Also, you could make the calls explicit by using the Java Object Integer and Long, and let autoboxing take care of the conversion to primitive types:

min(new Long(100))
min(new Integer(100))
CaTalyst.X
  • 1,645
  • 13
  • 16