java 编译器错误:对调用不明确的引用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14053596/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 14:53:48  来源:igfitidea点击:

Compiler error : reference to call ambiguous

javamethodspolymorphismjava-7boxing

提问by Ravi

Case 1

情况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

案例 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 ?

显示编译错误reference to call ambiguous。但是,我无法理解。为什么 ?但是,当我注释掉 中的任何call()方法时Case 2,它工作正常。谁能帮我理解,这里发生了什么?

采纳答案by assylias

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.

在 Java 语言规范 (JLS) 中以非常正式的方式定义了查找最具体的方法。我在尝试尽可能多地删除正式公式时提取了适用的主要项目。

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.

第三阶段(第 15.12.2.4 节)允许将重载与可变数量方法、装箱和拆箱相结合。

  • Then JLS 15.12.2.4basically 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:
  • 那么JLS 15.12.2.4基本确定这两种方法都适用,因为10可以转换为anInteger...或an int...。到现在为止还挺好。该段总结道:

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

在适用的可变元方法中选择最具体的方法(第 15.12.2.5 节)。

  • 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:
  • 这将我们带到了JLS 15.12.2.5。本段给出了一种元数方法m(a...)比另一种元数方法更具体的条件m(b...)。在您使用一个参数而没有泛型的用例中,它归结为:

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

m(a...)m(b...)iif更具体a <: b, where<:意味着is a subtype of

It happens that intis not a subtype of Integerand Integeris not a subtype of int.

它发生int是不是一个亚型IntegerInteger是不是一个亚型int

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

因此,为了使用 JLS 语言,两种call方法都具有最大的特定性(没有一种方法比另一种更特定)。在这种情况下,同一段落的结论是:

  • 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.
  • 如果所有最大的特定方法都具有覆盖等效(第 8.4.2 节)签名 [...] => 不是您的情况,因为不涉及泛型并且 Integer 和 int 是不同的参数
  • 否则,我们说方法调用不明确,并发生编译时错误。

NOTE

笔记

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

如果更换Integer...long...,例如,你必须int <: long和最具体的方法是call(int...)*。
同样,如果您替换int...Number...,则该call(Integer...)方法将是最具体的。

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

*在Java 7 之前的 JDK 中实际上存在一个错误,在这种情况下会显示不明确的调用

回答by Jayamohan

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

看起来它与错误 #6886431 有关,该错误似乎已在 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).

javac 将第二个视为比第一个更具体。这种行为是明智的(我更喜欢),但与 JLS (15.12.2) 不一致。

回答by radai

from JLS 15.12.2.2

来自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.

JLS 15.12.2.2 选择最具体的方法

如果多个方法声明既可访问又适用于方法调用,则必须选择一个方法声明来为运行时方法分派提供描述符。Java 编程语言使用选择最具体的方法的规则。非正式的直觉是,如果第一个方法处理的任何调用都可以传递给另一个方法而没有编译时类型错误,那么一个方法声明比另一个方法声明更具体。

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

这些方法都不能传递给另一个( int[] 和 Integer[] 的类型不相关)因此调用是不明确的

回答by aphex

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

这是因为编译器尝试将 Integer 转换为 int,因此,在调用该call方法之前会进行隐式转换。因此,编译器然后查找该名称的任何可以采用整数的方法。你有两个,所以编译器不知道应该调用哪一个。

回答by mishadoff

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

如果不止一种方法可以适用,那么从 Java 语言规范中我们选择最具体的方法,段落15.12.2.5

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

m如果满足以下任一条件,则命名的一个变量 arity 成员方法比另一个同名的变量 arity 成员方法更具体<: 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)
  1. 一个成员方法有 n 个参数,另一个有 k 个参数,其中 n ≥ k,并且:
    • 第一个成员方法的参数类型为 T1, ..., Tn-1, Tn[]。(我们只有一个 T_n[],也就是 Integer[], n=1
    • 另一种方法的参数类型为 U1, ..., Uk-1, Uk[]。(同样只有一个参数,即 int[], k=1
    • 如果第二种方法是泛型的,那么让 R1 ... Rp (p ≥ 1) 是它的类型参数,让 Bl 是 Rl (1 ≤ l ≤ p) 的声明边界,让 A1 ... Ap 是推断的类型参数(第 15.12.2.7 节)对于在初始约束 Ti << Ui (1 ≤ i ≤ k-1) 和 Ti << Uk (k ≤ i ≤ n) 下的调用,并让 Si = Ui[R1=A1,。 ..,Rp=Ap] (1 ≤ i ≤ k)。(方法不是通用的
    • 否则,让 Si = Ui (1 ≤ i ≤ k)。( S1 = int[])
    • 对于从 1 到 k-1 的所有 j,Tj <: Sj, and, (这里什么都没有)
    • 对于所有从 k 到 n 的 j,Tj <: Sk, and, (比较 T1 <: S1, Integer[] <: int[])
    • 如果第二种方法是如上所述的通用方法,则 Al <: Bl[R1=A1,...,Rp=Ap] (1 ≤ l ≤ p)。(方法不是通用的

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

虽然原语int被自动装箱到 wrapper Integer,但int[]没有被自动装箱到Integer[],比第一个条件不成立。

Second condition is almost the same.

第二个条件几乎相同。

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

还有其他条件不成立,然后由于JLS:

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

我们说方法调用不明确,会出现编译时错误。

回答by Raffaele

This question has already been askeda 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

这个问题已经被问过很多次了。棘手的部分是f(1, 2, 3)显然传递了int's,那么为什么编译器不能选择f(int...)版本?答案必须在JLS 中的某个地方,我对此很头疼

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

根据第 15.12.2.4 节,这两种方法都是适用的可变元方法,因此下一步是确定最具体的方法。

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

不幸的是,第15.12.2.5 节使用f1(T 1, .. T n)f2(S 1, .. S n)形式参数之间的子类型测试T i<: S i来识别目标方法,并且由于存在和之间没有子类型关系,没有人获胜,因为int :> IntegerInteger :> int 都没有。段末指出:Integerint

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

A method m1 is strictly more specificthan 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 specificfor 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.

上述条件是一种方法可能比另一种方法更具体的唯一情况。[...]

当且仅当 m1 比 m2 更具体且 m2 不比 m1 更具体时,方法 m1严格地比另一个方法 m2 更具体。

如果一个方法是可访问的和适用的,并且没有其他更严格的适用和可访问的方法,则该方法被称为对方法调用具有最大的特定性。

可能没有最具体的方法,因为有两种或更多方法是最具体的。在这种情况下:

  1. [...]

  2. 否则,我们说方法调用不明确,并发生编译时错误。

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

附上Gilad Bracha的一篇博客文章(见图表 2),然后链接到@Jayamhona 的回答中的错误报告。