Compiler error : reference to call ambiguous

2020-01-26 09:50发布

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 ?

6条回答
Fickle 薄情
2楼-- · 2020-01-26 10:06

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.

查看更多
▲ chillily
3楼-- · 2020-01-26 10:10

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.

查看更多
放我归山
4楼-- · 2020-01-26 10:13

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

查看更多
Fickle 薄情
5楼-- · 2020-01-26 10:13

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.

查看更多
我只想做你的唯一
6楼-- · 2020-01-26 10:14

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).

查看更多
够拽才男人
7楼-- · 2020-01-26 10:16

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.

查看更多
登录 后发表回答