12

Consider the following example code:

public class TestClass {

    public void doSth(String str, String l, Object... objects) {
        System.out.println("A");
    }

    public void doSth(String str, Object... objects) {
        System.out.println("B");
    }

}

When I now call new TestClass().doSth("foo", "bar") I get the expected result A. But if I change the method signature of the first method by chaging the parameter l to a primitive type:

public class TestClass {

    public void doSth(String str, long l, Object... objects) {
        System.out.println("A");
    }

    public void doSth(String str, Object... objects) {
        System.out.println("B");
    }

}

calling new TestClass().doSth("foo", 2L) will yield a reference to call ambiguous compile time error.

I thought about that one for some time now and also consulted this stackoverflow question, but I was unable to understand why this happens. In my opinion the doSth("foo", 2L) is more specific to the doSth(String string, long l, Object... obj) signature and should allow the compiler to also come to this conclusion.

Community
  • 1
  • 1
Entrusc
  • 197
  • 2
  • 11
  • Evidently primitives can also be Objects? – Rabbit Guy May 19 '16 at 20:19
  • @blahfunk, yes, they may be wrapped – Andrew Tobilko May 19 '16 at 20:20
  • The only explanation that comes to mind is [Autoboxing](https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html), but this should be one conversion-step "farther" away than the primitive version of the method. – Turing85 May 19 '16 at 20:20
  • I have no idea, but changing `long` to `Long` in the function declaration solved it for me. – UDKOX May 19 '16 at 20:22
  • 2
    Looks like a duplicate of http://stackoverflow.com/questions/17632741/when-overloading-compiler-does-not-prefer-primitive-to-object-only-in-case-of-va: *the methods contains varargs arguments and the compiler needs to get to the third phase trying to distinguish between them* – Tunaki May 19 '16 at 20:32

2 Answers2

5

In this case, auto-boxing is causing you grief. Ironically, before that you're correct - the "long" version would have easily been picked.

Basically the compiler knows that it can create a Long from your value which, of course, is an Object. So it is still confused as either the long or the Long version could be used. Is one "better" than the other? Maybe but it is a pretty fine line.

stdunbar
  • 16,263
  • 11
  • 31
  • 53
  • 1
    Well, [phase 1](http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2) in the JLS explicitely specifies *performs overload resolution without permitting boxing or unboxing conversion*. In this phase, autoboxing is not done. It looks like `doSth(String str, long l, Object... objects)` should be chosen in that phase. Am I missing something? – Tunaki May 19 '16 at 20:21
  • This is funny. Why does this not cause havoc, when there is only one parameter, i.e. `doSth(long l)` and `doSth(Object o)`? – Turing85 May 19 '16 at 20:22
  • 3
    @Tunaki Phase 1 and 2 also exclude variable arity methods so neither of OP's methods are considered until phase 3. Curiously, `doSth("foo", 2L, null)` will compile because of what the italicized note under the 1st bullet (in your link) states. – Radiodef May 19 '16 at 20:36
1

At this state, I can only report my observation, not the exact argumentation as to WHY Java behaves, like it does.

First, changing the methods to

void doSth(long l) {...}
void doSth(Object o) {...}

gets rid of the problem, i.e. doSth(2L); will yield the expected result.

Going one step further, changing the method parameter to varargs

void doSth(long... ls) {...}
void doSth(Object... os) {...}

together with the call doSth(2l); yields the same compilation error as reported by OP.

My suggestion at this stage is that encapusalting the parameter into an array, together with Autoboxing causes the havoc. My knowledge about the JLS is not firm enough to explain this properly.

Turing85
  • 18,217
  • 7
  • 33
  • 58