public class Primitive {
void m(Number b, Number ... a) {} // widening, autoboxing->widening->varargs
void m(byte b, Number ... a) {} // unboxing, autoboxing->widening->varargs
public static void main(String[] args) {
Byte b = 12;
Primitive obj = new Primitive();
obj.m(b, 23);
}
}我已经搜索并发现,拓宽优先级高于取消装箱,因此在上述方法调用中,应该调用第一个方法,因为第二个参数对这两个方法都是相同的。但这种情况不会发生。你能解释一下吗?
发布于 2014-04-12 13:06:54
它无法在JDK1.5、1.6和1.7中编译,但在JDK1.8中工作。
Update:它与第一个JDK8版本一起工作似乎实际上是一个bug:它在JDK 1.8.0_05中工作,但是根据这个问题和medvedev1088的答案,这段代码将不再在1.8.0_25中编译,这是一种符合JLS的行为。
我不认为这是一个被修复的bug。相反,这是与Java 8中的lambda表达式的方法调用机制相关的更改的结果。
大多数人可能会同意,关于“方法调用表达式”的部分是Java语言规范中最复杂、最难以理解的部分。而且可能有一整组工程师关心交叉核对和验证这一部分。因此,任何陈述或任何试图推理的行为都应该以一种大幅度的含糊其辞的方式进行。(即使是来自上述工程师)。但我将尝试一下,至少把其他人可能参考的相关部分具体化,以便作进一步分析:
考虑到关于
考虑到这两种方法都是“潜在适用的方法”( JLS7 / JLS8 ),那么相关的分节就是关于
对于JLS 7,它声明
方法m是一种适用的变量性方法,当且仅当下列所有条件都成立:
(其他条件是指这里不相关的调用形式,例如真正使用varargs的调用,或者涉及泛型的调用)
参考示例:当可以通过方法调用转换将b转换为相应的形式方法参数时,方法适用于Byte类型的实际参数b。根据有关方法调用转换在JLS7中的相应部分,允许进行以下转换:
显然,根据本规范,有两种方法是适用的:
m(Number b, Number ... a)可以通过扩大引用转换来适用。m(byte b, Number ... a)可通过取消装箱转换而适用。您提到了“扩展优先级高于取消装箱的...found”,但这在这里不适用:上面列出的条件不涉及任何“优先级”。它们被列为不同的选项。即使第一种方法是void m(Byte b, Number ... a),“身份转换”也是适用的,但它仍然只能算作一种可能的转换,并由于模糊性而导致错误方法。
因此,据我所知,这解释了为什么而不是与JDK7一起工作。我没有详细了解为什么与JDK8一起工作。但在识别JLS 8中变量性调用所适用的方法中,变量方法适用性的定义略有变化。
如果m不是泛型方法,那么如果对于1≤i≤k,ei在松散调用上下文中与Ti兼容,或者ei与适用性无关(第15.12.2.2节),则m适用于变量性调用。
(我还没有深入研究“松散调用上下文”和第15.12.2.2节的定义,但这似乎是这里的关键区别)
顺便提一句,再一次引用您的声明,即“扩展优先级高于取消装箱的...found”:对于做而不是的方法来说,这是正确的(并且根本不需要方法调用转换)。如果您忽略了示例中的变量,那么查找匹配方法的过程将从第一阶段:通过子类型确定适用的匹配性方法开始。然后,由于m(Number b)是Number的一个子类型,因此该方法已经适用于参数Byte b。没有理由进入第二阶段:通过方法调用转换确定适用的匹配性方法。在此阶段,将应用通过取消装箱从Byte到byte的方法调用转换,但从未达到此阶段。
https://stackoverflow.com/questions/23020493
复制相似问题