Compiler error : reference to call ambiguous
Asked Answered
O

6

25

Case 1

static void call(Integer i) {
    System.out.println("hi" + i);
}

static void call(int i) {
    System.out.println("hello" + i);
}

public static void main(String... args) {
    call(10);
}

Output of Case 1 : hello10

Case 2

static void call(Integer... i) {
    System.out.println("hi" + i);
}

static void call(int... i) {
    System.out.println("hello" + i);
}

public static void main(String... args) {
    call(10);
}

Shows compilation error reference to call ambiguous. But, I was unable to understand. Why ? But, when I commented out any of the call() methods from Case 2, then It works fine. Can anyone help me to understand, what is happening here ?

Ordinand answered 27/12, 2012 at 11:2 Comment(2)
what method do you think it should be invoked - in order the case not be ambiguous?Colocynth
call(int... i), because it is more specific.Ordinand
F
15

Finding the most specific method is defined in a very formal way in the Java Language Specificaion (JLS). I have extracted below the main items that apply while trying to remove the formal formulae as much as possible.

In summary the main items that apply to your questions are:

The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.

  • Then JLS 15.12.2.4 basically determines that both method are applicable, because 10 can be converted to both an Integer... or an int.... So far so good. And the paragraph concludes:

The most specific method (§15.12.2.5) is chosen among the applicable variable-arity methods.

  • Which brings us to JLS 15.12.2.5. This paragraph gives the conditions under which an arity method m(a...) is more specific than another arity method m(b...). In your use case with one parameter and no generics, it boils down to:

m(a...) is more specific than m(b...) iif a <: b, where <: means is a subtype of.

It happens that int is not a subtype of Integer and Integer is not a subtype of int.

To use the JLS language, both call methods are therefore maximally specific (no method is more specific than the other). In this case, the same paragraph concludes:

  • If all the maximally specific methods have override-equivalent (§8.4.2) signatures [...] => not your case as no generics are involved and Integer and int are different parameters
  • Otherwise, we say that the method invocation is ambiguous, and a compile-time error occurs.

NOTE

If you replaced Integer... by long... for example, you would have int <: long and the most specific method would be call(int...)*.
Similarly, if you replaced int... by Number..., the call(Integer...) method would be the most specific.

*There was actually a bug in JDKs prior to Java 7 that would show an ambiguous call in that situation.

Fimbria answered 2/1, 2013 at 14:56 Comment(1)
It's worth remarking that the reason case 1 is not considered ambiguous is also described in JLS §15.12.2: "This guarantees that any calls that were valid in the Java programming language before Java SE 5.0 are not considered ambiguous as the result of the introduction of variable arity methods, implicit boxing and/or unboxing." Before Java 5, there was no implicit boxing, and call(Integer i) would not be considered, hence no ambiguity.Eroto
A
4

Looks like it's related to bug #6886431, which seems to be fixed in OpenJDK 7.

Below is the bug description,

Bug Description:

When invoking a method with the following overloaded signatures, I expect an ambiguity error (assuming the arguments are compatible with both):

int f(Object... args);
int f(int... args);

javac treats the second as more specific than the first. This behavior is sensible (I prefer it), but is inconsistent with the JLS (15.12.2).

Assyriology answered 27/12, 2012 at 11:17 Comment(7)
Which means, its bug of java ??Ordinand
Which version of Java you are using. If possible provide your eclipse version too.Assyriology
the bug you mentioed makes things compile when they shouldnt. this isnt it.Soviet
Please don't guess your versions. Be specific. I requested your eclipse version too(If you are using Eclipse).Assyriology
Its jdk7, i just guess, because, i didn't remember exact version. I mean, JDK 1.7.0_19Ordinand
Although this is related, it is because the bug has been fixed that the call is ambiguous. So this does not really answer the question.Fimbria
This problem exists in JDK8u20Anzovin
S
2

from JLS 15.12.2.2

JLS 15.12.2.2 Choose the Most Specific Method

IIf more than one method declaration is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen. The informal intuition is that one method declaration is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time type error.

neither of these methods can be passed to the other (the types for int[] and Integer[] arent related) hence the call is ambiguous

Soviet answered 27/12, 2012 at 11:40 Comment(1)
how about double[] and int[]: these aren't related either but replacing Integer by double would remove the compile error (in JDK 7+).Fimbria
T
1

The compiler doesn't know which method should be called. In order to fix this, you need to cast the input parameters..

public static void main(String... args) {
  call((int)10);
  call(new Integer(10));
}

EDIT:

It is because the compiler tries to convert the Integer into int, Therefore, an implicit cast takes place prior to invocation of the call method. So the compiler then looks for any methods by that name that can take ints. And you have 2 of them, so the compiler doesn't know which of both should be called.

Thumbsdown answered 27/12, 2012 at 11:7 Comment(1)
you just told the solution. My question was why ?? why it is not working ?? Any reference ?Ordinand
P
0

If more than one method can be applicable, than from the Java Language Specification we Choosing the Most Specific Method, paragraph 15.12.2.5:

One variable arity member method named m is more specific than another variable arity member method of the same name if either (<: means subtyping):

  1. One member method has n parameters and the other has k parameters, where n ≥ k, and:
    • The types of the parameters of the first member method are T1, ..., Tn-1, Tn[]. (we have only one T_n[], which is Integer[], n=1)
    • The types of the parameters of the other method are U1, ..., Uk-1, Uk[]. (again only one paramenter, which is int[], k=1)
    • If the second method is generic then let R1 ... Rp (p ≥ 1) be its type parameters, let Bl be the declared bound of Rl (1 ≤ l ≤ p), let A1 ... Ap be the type arguments inferred (§15.12.2.7) for this invocation under the initial constraints Ti << Ui (1 ≤ i ≤ k-1) and Ti << Uk (k ≤ i ≤ n), and let Si = Ui[R1=A1,...,Rp=Ap] (1 ≤ i ≤ k). (method is not generic)
    • Otherwise, let Si = Ui (1 ≤ i ≤ k). (S1 = int[])
    • For all j from 1 to k-1, Tj <: Sj, and, (nothing here)
    • For all j from k to n, Tj <: Sk, and, (Compare T1 <: S1, Integer[] <: int[])
    • If the second method is a generic method as described above, then Al <: Bl[R1=A1,...,Rp=Ap] (1 ≤ l ≤ p). (method is not generic)

Although primitive int is autoboxed to wrapper Integer, int[] is not autoboxed to Integer[], than the first condition doesn't hold.

Second condition is almost the same.

There are also other conditions that do not hold, and then due to JLS:

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

Prink answered 27/12, 2012 at 11:50 Comment(3)
You make a confusion: S1 is int, not int[].Fimbria
@Fimbria S1 one of the method parameters, method have just one parameter int[]Prink
Si is defined as Si = Ui and U is defined as U1, ..., Uk-1, Uk[] => Uk[] <=> int[] => Uk <=> int. If it were not the case, m(Number...) would not be more specific than m(Object...) for example, because (following your reasoning) Number[] is not a subtype of Object[].Fimbria
C
0

This question has already been asked a number of times. The tricky part is that f(1, 2, 3) is clearly passing int's, so why can't the compiler pick the f(int...) version? The answer must lie somewhere in the JLS, which I'm scratching my heads against

According to §15.12.2.4, both methods are applicable variable-arity method, so the next step is identifying the most specific one.

Unofortunately, §15.12.2.5 uses the subtype test Ti <: Si between f1(T1, .. Tn) and f2(S1, .. Sn) formal parameters to identify the target method, and since there is no subtype relationship between Integer and int, no one wins, because neither int :> Integer nor Integer :> int. At the end of the paragraph is stated:

The above conditions are the only circumstances under which one method may be more specific than another. [...]

A method m1 is strictly more specific than another method m2 if and only if m1 is more specific than m2 and m2 is not more specific than m1.

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:

  1. [...]

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

Attached a blog post by Gilad Bracha (see exhibit 2), in turn linked in the bug report from the @Jayamhona's answer.

Colcannon answered 27/12, 2012 at 12:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.