编译器错误:引用调用暧昧(Compiler error : reference to call am

2019-07-17 15: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 。 但是,我无法理解。 为什么呢? 但是,当我注释掉任何的call()方法,从Case 2 ,然后它工作正常。 谁能帮我明白了,发生了什么吗?

Answer 1:

寻找最具体的方法是在Java语言Specificaion(JLS)很正式地定义。 我已经提取低于适用,同时试图尽可能多地去除正式公式的主要项目。

综上所述适用于您的问题的主要项目有:

  • JLS 15.12.2 :你的使用情况下,属于第三阶段:

第三阶段(§15.12.2.4)允许重载具有可变元数的方法,拳击,和取消装箱组合。

  • 然后JLS 15.12.2.4基本上确定两个方法都适用,因为10可以转换为两个的Integer...int... 。 到现在为止还挺好。 和段落的结论:

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

  • 这给我们带来JLS 15.12.2.5 。 本段给出下其中元数法的条件m(a...)比另一元数的方法更具体的m(b...) 在您的使用情况下,有一个参数,并没有仿制药,把它归结为:

m(a...)比更具体的m(b...) IIF a <: b ,其中<:装置is a subtype of

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

要使用JLS语言,无论call方法因此最大程度地特定的(没有方法比其他更具体的)。 在这种情况下,同款的结论:

  • 如果所有的最大具体方法有覆盖当量(§8.4.2)签名[...] =>不是你的情况下,因为没有仿制药参与和Integer和INT不同的参数
  • 否则,我们说该方法调用是不明确的,并发生编译时错误。

注意

如果换成Integer...通过long...例如,你会int <: long和最具体的方法是call(int...) *。
同样,如果您更换int...Number...call(Integer...)方法将是最具体的。

*有其实在的JDK之前的Java 7中的错误,将显示在这种情况下不明确的呼叫 。



Answer 2:

看起来像它涉及到的bug#6886431 ,这似乎是固定的OpenJDK 7。

下面是错误描述,

错误描述:

当调用与下面重载签名的方法,我期望的歧义错误(假设参数是与两个兼容):

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

的javac会将第二个作为比所述第一更具体。 此行为是合理的(我喜欢),但与JLS(15.12.2)不一致。



Answer 3:

从JLS 15.12.2.2

JLS 15.12.2.2选择最具体方法

I如果超过一个方法声明既方便和适用于方法调用,就必须选择一个提供运行时方法调度描述符。 Java编程语言使用的是选择了最具体方法的规则。 非正式直觉是一个方法的声明比另一种更具体的,如果通过第一种方法处理的任何调用可以被传递到另一个没有编译时类型错误。

这些方法都没有可以被传递到另一个(类型对于int []和Integer []的arent相关),因此该呼叫是不明确的



Answer 4:

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

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

编辑:

这是因为编译器试图将整数转换成整型,因此,隐式转换发生在的调用之前call方法。 因此,编译器然后查找该名称,可以采取整数任何方法。 而你对他们有2,所以编译器不知道哪个都应该被调用。



Answer 5:

如果有多个方法可以适用,不是从Java语言规范 ,我们选择最具体方法 ,段15.12.2.5

一个可变参数数量构件的方法命名为m比相同名称的另一个变量元数构件方法更具体如果( <: means subtyping ):

  1. 一种构件方法具有n个参数和其它的有k个参数,其中n≥k,以及:
    • 类型的第一构件方法的参数是T1,...,TN-1,TN []。 ( 我们只有一个T_n [],这是整数[]中,n = 1)
    • 类型的其他方法的参数的是U1,...,UK-1,UK []。 ( 再次仅一个paramenter,其为int [],K = 1)
    • 如果第二方法是通用然后让R1 ...卢比(P≥1)是它的类型参数,让B1中是宣告结合器R1(1≤升≤P),让A1 ...鸭是类型参数推断(§15.12.2.7)根据初始约束的Ti << UI这个调用(1≤I≤K-1)和Ti <<屋(K≤I≤n),以及让硅= UI [R1 = A1 ,. ..,RP = AP](1≤I≤K)。 ( 方法没有通用
    • 否则,令的Si = UI(1≤I≤K)。 (S1 = INT [])
    • 对于从1所有的j到k-1,TJ <:SJ,和,( 没有在这里
    • 为选自K至n所有的j,TJ <:Sk,并且,( 比较T1 <:S1,整数[] <:INT [])
    • 如果第二方法是,如上所述,一个通用的方法则铝<:BL [R1 = A1,...,RP = AP](1≤升≤P)。 ( 方法没有通用

尽管原始int被autoboxed到包装器Integerint[]未autoboxed到Integer[]比所述第一条件不成立。

第二个条件是几乎一样的。

也有不持有其他条件,然后由于JLS:

我们说该方法调用是不明确的,并发生编译时错误。



Answer 6:

这个问题已经被问了很多次。 最棘手的部分是, f(1, 2, 3)清楚地传递int的,那么为什么不能编译挑f(int...)版本? 答案一定的某处在JLS ,我敢抓我的头反对

据§15.12.2.4,这两种方法均适用可变元数的方法 ,因此,下一个步骤是确定最特定的一个。

Unofortunately, §15.12.2.5使用亚型测试T I <:S I F1(T 1,... T N)f2之间(S 1,... S n中)的形式参数,以确定所述目标方法,并且由于有之间不存在子类型关系Integerint ,没有一个获胜 ,因为既不INT:>整数也不整数:> int值 。 在该段的最后陈述:

上述条件是唯一的情况下,其中一个方法可以比另一个更具体的。 [...]

一种方法,m1是严格大于另一种方法m2 更明确当且仅当m1比m2更明确和m2是不大于M1更具体。

一种方法被认为是最大限度地特定的方法调用,如果它是方便和适用,没有其他是适用的,可访问是严格更具体的方法。

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

  1. [...]

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

附博客文章由吉拉德·布拉彻(见图表2),依次从@ Jayamhona的答案错误报告链接。



文章来源: Compiler error : reference to call ambiguous