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);
}
}
我已经搜索过,发现加宽优先级高于拆箱,因此在上述方法调用中,应该调用第一个方法,因为第二个参数对于两个都相同。但这不会发生。你能解释一下吗?
最佳答案
它无法在JDK 1.5、1.6和1.7中进行编译,但可以在JDK 1.8中使用。
Update: It seems like the fact that it worked with the first JDK8 versions was actually a bug: It worked in JDK 1.8.0_05, but according to this question and the answer by medvedev1088, this code will no longer compile in 1.8.0_25, which is the behavior that conforms to the JLS
我认为这不是已修复的错误。相反,它是与Java 8中lambda表达式的方法调用机制相关的更改的影响。
大多数人可能会同意,关于“方法调用表达式”的部分是迄今为止Java语言规范中最复杂,最难以理解的部分。可能会有整个工程师团队负责交叉检查和验证此部分。因此,任何陈述或任何试图进行的推理都应花费大量的盐。 (即使来自上述工程师)。但我会尝试一下,至少充实其他人可能会引用以进行进一步分析的相关部分:
考虑有关
JLS 7中的
并且考虑到这两种方法都是“潜在适用方法”(JLS7/JLS8),则相关的小节是关于
JLS7中的
对于JLS 7,它指出
The method m is an applicable variable-arity method if and only if all of the following conditions hold:
- For 1 = i < n, the type of ei, Ai, can be converted by method invocation conversion to Si.
- ...
(其他条件指的是此处不相关的调用形式,例如真正使用varargs的调用或涉及泛型的调用)
参见示例:当
b
可以通过方法调用转换转换为相应的形式方法参数时,方法适用于Byte
类型的实际参数表达式b
。根据JLS7中有关Method Invocation Conversion的相应部分,允许进行以下转换:显然,根据此规范,可以采用两种方法:
m(Number b, Number ... a)
可通过扩展引用转换m(byte b, Number ... a)
可通过取消装箱转换您提到您“...发现加宽优先级高于拆箱”,但这在此处不适用:上面列出的条件不涉及任何“优先级”。它们被列为不同的选项。即使第一种方法是
void m(Byte b, Number ... a)
,“身份转换”也将适用,但是它仍然仅算作一种可能的转换,并且由于含糊不清而导致错误方法。因此,据我所知,这解释了为什么它与JDK7一起使用而不是。我没有弄清楚为什么与JDK8一起工作的原因。但是在Identify Methods Applicable by Variable Arity Invocation in JLS 8中,可变Arity方法的适用性定义发生了明显变化:
If m is not a generic method, then m is applicable by variable arity invocation if, for 1 ≤ i ≤ k, either ei is compatible in a loose invocation context with Ti or ei is not pertinent to applicability (§15.12.2.2).
(我尚未深入研究“松散调用上下文”的定义和第15.12.2.2节,但这似乎是关键的区别)
顺便说一句,再次提到您“...发现扩展优先级高于拆箱优先级”的声明:对于执行而不是涉及vararg的方法(这完全不需要方法调用转换)来说,这是正确的。如果在示例中省略了变量,那么查找匹配方法的过程将从Phase 1: Identify Matching Arity Methods Applicable by Subtyping开始。由于
m(Number b)
是Byte b
的子类型,因此Byte
方法将已经适用于参数Number
。没有理由进入Phase 2: Identify Matching Arity Methods Applicable by Method Invocation Conversion。在此阶段,将应用通过从Byte
到byte
取消装箱的方法调用转换,但是此阶段从未实现。
关于java - 为什么此方法重载模棱两可?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23020493/