【问题标题】:Compiler error : reference to call ambiguous编译器错误:对调用不明确的引用
【发布时间】:2012-12-27 11:02:28
【问题描述】:

案例 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);
}

案例 1 的输出:hello10

案例 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);
}

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

【问题讨论】:

  • 你认为应该调用什么方法 - 为了使情况不模棱两可?
  • call(int... i),因为它更具体。

标签: java methods polymorphism java-7 boxing


【解决方案1】:

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

总的来说,适用于您的问题的主要项目是:

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

  • 那么JLS 15.12.2.4基本确定这两种方法都适用,因为10可以同时转换为Integer...int...。到目前为止,一切都很好。该段的结尾是:

最具体的方法(§15.12.2.5)是在适用的可变参数方法中选择的。

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

m(a...)m(b...) iif a <: b 更具体,其中<: 表示is a subtype of

碰巧int 不是Integer 的子类型,而Integer 不是int 的子类型。

要使用 JLS 语言,call 方法因此具有最大的特异性(没有一种方法比另一个更具体)。在这种情况下,同一段的结论是:

  • 如果所有最具体的方法都具有重写等效 (§8.4.2) 签名 [...] => 不是您的情况,因为不涉及泛型并且 Integer 和 int 是不同的参数
  • 否则,我们说方法调用有歧义,出现编译时错误。

注意

例如,如果您将Integer... 替换为long...,您将拥有int <: long,最具体的方法将是call(int...)*。
同样,如果您将int... 替换为Number...,则call(Integer...) 方法将是最具体的。

*原来有a bug in JDKs prior to Java 7 that would show an ambiguous call in that situation

【讨论】:

  • 值得注意的是,案例 1 被认为不明确的原因也在 JLS §15.12.2 中进行了描述:“这保证了在 Java 编程中有效的任何调用由于引入了可变参数方法、隐式装箱和/或拆箱,Java SE 5.0 之前的语言不被认为是模棱两可的。”在 Java 5 之前,没有隐式装箱,不会考虑 call(Integer i),因此没有歧义。
【解决方案2】:

看起来它与bug #6886431 有关,这似乎在 OpenJDK 7 中已修复。

以下是错误描述,

错误说明:

当调用具有以下重载签名的方法时,我 预计会出现歧义错误(假设参数与 两者):

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

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

【讨论】:

  • 也就是说,它是java的bug??
  • 您使用的是哪个版本的 Java。如果可能的话,也提供你的 eclipse 版本。
  • 你提到的错误使事情在不应该编译的时候编译。这不是它。
  • 请不要猜测您的版本。请明确点。我也请求了你的 Eclipse 版本(如果你使用的是 Eclipse)。
  • 它的 jdk7,我只是猜测,因为,我不记得确切的版本。我的意思是,JDK 1.7.0_19
【解决方案3】:

来自JLS 15.12.2.2

JLS 15.12.2.2 选择最具体的方法

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

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

【讨论】:

  • double[]int[] 怎么样:它们也不相关,但将 Integer 替换为 double 将消除编译错误(在 JDK 7+ 中)。
【解决方案4】:

编译器不知道应该调用哪个方法。为了解决这个问题,您需要转换输入参数..

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

编辑:

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

【讨论】:

  • 你刚刚告诉了解决方案。我的问题是为什么?为什么它不工作?有参考吗?
【解决方案5】:

如果可以应用不止一种方法,那么来自 Java 语言规范我们Choosing the Most Specific Method,段落15.12.2.5

一个名为m 的变量arity 成员方法比另一个同名的变量arity 成员方法更具体,如果(<: means subtyping):

  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 为在初始约束 Ti 方法不是通用的)
    • 否则,令 Si = Ui (1 ≤ i ≤ k)。 (S1 = int[])
    • 对于从 1 到 k-1 的所有 j,Tj <: sj>这里没有)
    • 对于从 k 到 n 的所有 j,Tj <: sk and>Compare T1 <: s1 integer int>)
    • 如果第二种方法是如上所述的通用方法,则 Al <: bl l p>方法不是通用的)

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

第二个条件差不多。

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

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

【讨论】:

  • 你搞错了:S1int,而不是 int[]
  • @assylias S1方法参数之一,方法只有一个参数int[]
  • Si 定义为Si = Ui,U 定义为U1, ..., Uk-1, Uk[] => Uk[] &lt;=&gt; int[] => Uk &lt;=&gt; int。如果不是这种情况,m(Number...) 不会比m(Object...) 更具体,因为(根据您的推理)Number[] 不是Object[] 的子类型。
【解决方案6】:

这个问题has already been asked 很多次。棘手的部分是f(1, 2, 3) 显然是通过int 的,那么为什么编译器不能选择f(int...) 版本呢?答案一定在JLS 的某个地方,我正在摸不着头脑

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

不幸的是,§15.12.2.5f1(T1 之间使用了子类型测试 Ti<: s>i , .. Tn) 和 f2(S1, .. Sn) 形式参数来标识目标方法,并且由于Integerint 之间没有子类型关系,所以没有一个获胜,因为int :> Integer 也不是 整数 :> int。在段落的末尾声明:

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

如果满足以下条件,则方法 m1 比另一种方法 m2 更具体 并且仅当 m1 比 m2 更具体且 m2 不是更具体时 大于 m1。

方法被称为最大程度地特定方法调用 如果它是可访问和适用的,并且没有其他方法可以 是适用和可访问的,严格来说更具体。

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

  1. [...]

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

由 Gilad Bracha 附加 a blog post(见附件 2),然后在来自 @Jayamhona 答案的错误报告中链接。

【讨论】:

    猜你喜欢
    • 2017-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多