17

It's just an exercise but I can't figure out the ambiguity:

private static void flipFlop(String str, int i, Integer iRef) {
System.out.println(str + "ciao");
}

private static void flipFlop(String str, int i, int j) {
System.out.println(str + "hello");
}

public static void main(String[] args) {
flipFlop("hello", new Integer(4), 2004);
}

It says:

The method flipFlop(String, int, Integer) is ambiguous for the type Test

I would have guessed that the second argument would have been unwrapped to int and so the second flipFlop method would have been the choice.

Jeroen Vannevel
  • 43,651
  • 22
  • 107
  • 170
dierre
  • 7,140
  • 12
  • 75
  • 120
  • 3
    the last argument can be boxed into an `Integer` or applied as a primitive – Reimeus Jan 04 '14 at 17:01
  • 7
    @Kayaman: link it. We all know it's probably somewhere in the JLS, just saying it's in there doesn't add any value. – Jeroen Vannevel Jan 04 '14 at 17:02
  • 3
    For the record it compiles fine with Java 8 and prints "hellohello". – assylias Jan 04 '14 at 17:08
  • 1
    @assylias it would be great if you can find and add here the Java 8 JLS reference that makes unambiguous. – Miserable Variable Jan 04 '14 at 17:30
  • @MiserableVariable I have observed that it compiles and runs fine but I haven't checked the JLS (which is not centralised in one single document yet as far as I know). – assylias Jan 04 '14 at 17:41
  • @MiserableVariable: I have been looking for it for some time but couldn't find it. Since the Java 8 JLS hasn't been released in its entirety yet (only a few drafts so far), it's not in there as far as I can tell. – Jeroen Vannevel Jan 04 '14 at 17:41
  • @JeroenVannevel I guess unboxing conversion has to be preferable over boxing conversion to make this happen, correct? – Miserable Variable Jan 07 '14 at 21:41
  • 1
    @MiserableVariable: that would be my assumption as well, yes. They probably added that to Phase 2 (as described in my answer). – Jeroen Vannevel Jan 07 '14 at 21:52

3 Answers3

14

If you enjoy riveting reading, here is the relevant portion of the Java Language specification that describes how methods are resolved.

But basically your third argument can be interpreted as a primitive or an autoboxed wrapper, and the compiler can't figure out what you want. Both methods are "maximally specific" to use the JLS terminology.

Vidya
  • 29,932
  • 7
  • 42
  • 70
  • 5
    The reason why both are maximally specific is that neither `int <: Integer` nor `Integer <: int` (i.e. neither is a subtype of the other). – assylias Jan 04 '14 at 17:12
  • 1
    uhm...ok, the thing is that I assumed that since an int argument is available, the second method would have been chosen before the need to try the autoboxing on a primitive. – dierre Jan 04 '14 at 17:15
  • What I don't understand is why this compiles `flipFlop("hello", 4, new Integer(4));` ? But this doesn't `flipFlop("hello", new Integer(4), new Integer(4));` ? – user2336315 Jan 04 '14 at 17:17
  • 1
    It's because you don't have a `flipFlop(String, Integer, Integer)` method. – ADTC Jan 04 '14 at 17:19
  • 3
    @user2336315: the former has an exact match (the first method) whereas the latter has to decide between `int` or `Integer` for the last parameter: ambiguous. – Jeroen Vannevel Jan 04 '14 at 17:19
  • 1
    @JeroenVannevel Makes sense. Thanks! But isn't the compiler smart enough to take the closest match ? (And the answer is no, otherwise we don't have this error I suppose) I.e, the first method in this case too. – user2336315 Jan 04 '14 at 17:22
  • Doing that (_taking the closest match_) introduces uncertainty. That's why you are forced to make it unambiguous what exactly you want. – ADTC Jan 04 '14 at 17:42
11

Alright, I have taken a closer look at the JLS and I believe this should clear up any remaining doubts you might have.

This is the original problem:

public class Main {
    private static void flipFlop(int i, Integer iRef) {
        System.out.println("Method 1");
    }

    private static void flipFlop(int i, int j) {
        System.out.println("Method 2");
    }

    public static void main(String[] args) {
        flipFlop(new Integer(4), 2004);
    }
}

As has been pointed out in the other answer: this fails because the compiler can't decide what overload to use.

However you might think this doesn't make any sense. The compiler can decide just fine in this situation what method he should use:

public class Main {
    private static void flipFlop(Integer y) {
        System.out.println("ciao");
    }

    private static void flipFlop(int j) {
        System.out.println("hello");
    }

    public static void main(String[] args) {
        flipFlop(new Integer(6));
        flipFlop(6);
    }
}

Rationality tells us that when you have values X + Y and two methods that take respectively Y + X and Y + Y and you know that X and Y are interchangeable, then this would mean that the latter method is more specific.

The difference between these two are described in the JLS. I have provided the entire workflow below, but what matters is this:

First the compiler will look at methods with an equal signature while forbidding boxing/unboxing. In our second example this doesn't cause any problems, but in our first example this doesn't return a satisfiable method since neither of them take an Integer as the first parameter..

When that failed, the compiler moves on to the second step where it allows boxing/unboxing. This should fix the issue we had with the first parameter, but now causes ambiguity with the second parameter since it is now uncertain whether you're referring to the overload using an int or the one using an Integer.

This ultimately results in an ambiguous method call.

15.12.2. Compile-Time Step 2: Determine Method Signature

  1. The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.

  2. The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing, but still precludes the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the third phase.

If several applicable methods have been identified during one of the three phases of applicability testing, then the most specific one is chosen, as specified in section §15.12.2.5.

15.12.2.5. Choosing the Most Specific Method

A method is said to be maximally specific for a method invocation if it is accessible and applicable and there is no other method that is applicable and accessible that is strictly more specific.

It is possible that no method is the most specific, because there are two or more methods that are maximally specific. In this case:

  • If all the maximally specific methods have override-equivalent (§8.4.2) signatures, then: (... some rules to decide who's chosen ...)

  • Otherwise, we say that the method invocation is ambiguous, and a compile-time error occurs.

Community
  • 1
  • 1
Jeroen Vannevel
  • 43,651
  • 22
  • 107
  • 170
  • So basically the pre-boxing evaluation has been applied at signature level and not parameter by parameter, that's why my reasoning was not correct. – dierre Jan 04 '14 at 18:23
  • 1
    @dierre: In the first phase it prohibited boxing which caused ambiguity with the first parameter. In the second phase it allowed boxing, which caused ambiguity with the second parameter (since an `int` and and `Integer` are equal when boxing is allowed). That's pretty much it. – Jeroen Vannevel Jan 04 '14 at 18:28
  • I haven't tested it, but does the error go away if you code `flipFlop(new Integer(4), (int)2004))`? – Bohemian Jan 04 '14 at 20:21
  • @Bohemian: It doesn't. Which makes sense, since boxing is allowed in the second phase and it won't find a suitable method in the first phase. At that point it will find 2 possible candidates and it will try all possibilities as far as I can tell. – Jeroen Vannevel Jan 04 '14 at 21:31
0

Your second argument 2004 which is an int would also apply to an Integer because of auto-boxing, thats why the compiler can't decide which method to use.

Patrick
  • 33,984
  • 10
  • 106
  • 126