Ama*_*man 22 java primitive overloading jdk1.6 wrapper
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);
}
}
Run Code Online (Sandbox Code Playgroud)
我已经搜索过并发现扩展优先级高于取消装箱,因此在上面的方法调用中,应该调用第一个方法,因为第二个参数对于两者都是相同的.但这不会发生.你可以解释一下吗?
它无法在JDK 1.5,1.6和1.7中编译,但在JDK 1.8中工作.
更新:这似乎是一个事实,即它与第一JDK8版本的工作实际上是一个错误:它曾在JDK 1.8.0_05,但根据这个问题,并通过medvedev1088答案,这个代码将不会再在1.8.0_25编译,这是符合JLS的行为
我不认为这是一个修复过的错误.相反,它更像是与Java 8中的lambda表达式的方法调用机制相关的更改的效果.
大多数人可能会同意关于"方法调用表达式"的部分是迄今为止Java语言规范中最复杂的不可理解的部分.可能有一整个工程师团队负责交叉检查和验证这一部分.所以任何陈述或任何尝试的推理都应该带着巨大的影响.(即使它来自上述工程师).但我会试一试,至少要充实其他人可能会参考的相关部分,以便进一步分析:
考虑到有关的部分
并且考虑到这两种方法都是"潜在适用的方法"(JLS7/JLS8),那么相关的小节是关于
对于JLS 7,它表示
当且仅当满足以下所有条件时,方法m是适用的变量方法:
- 对于1 = i <n,可以通过方法调用转换为Si来转换ei的类型Ai.
- ...
(其他条件是指这里不相关的调用形式,例如真正使用 varargs的调用,或涉及泛型的调用)
参考示例:当可以通过方法调用转换转换为相应的形式方法参数时,方法适用于b类型的实际参数表达式.根据JLS7中有关方法调用转换的相应部分,允许以下转换:Byteb
显然,根据本规范有两种方法适用:
m(Number b, Number ... a) 适用于扩展参考转换m(byte b, Number ... a) 适用于拆箱转换您提到您"......发现扩大优先级高于取消装箱",但这不适用于此:上面列出的条件不涉及任何"优先级".它们被列为不同的选项.即使第一种方法是void m(Byte b, Number ... a),"身份转换"也适用,但它仍然只能算作一种可能的转换,并且由于模糊性而导致错误方法.
所以,据我所知,这解释了为什么它不适用于JDK7.我没有详细弄清楚为什么它没有与JDK8工作.但是,在JLS 8中可变Arity调用的识别方法中,变量arity方法的适用性定义略有变化:
如果m不是泛型方法,则m适用于变量arity调用,如果,对于1≤i≤k,ei在松散调用上下文中与Ti或ei兼容,则与适用性无关(第15.12.2.2节).
(我还没有深入研究"松散调用上下文"和第15.12.2.2节的定义,但这似乎是这里的关键区别)
另外,再次提到你的声明"你发现扩大优先级高于取消装箱":对于不涉及varargs的方法(并且根本不需要方法调用转换)也是如此.如果您在示例中省略了varags,那么找到匹配方法的过程将从第1阶段开始:识别匹配的Arty方法(Subtyping).m(Number b)然后该方法Byte b由于Byte是子类型而已经适用于该参数Number.没有理由进入阶段2:识别方法调用转换所适用的匹配Arity方法.在这个阶段,通过拆箱方法调用转换从Byte到byte会申请,但从来没有达到这个阶段.