它无法在 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 是适用的可变 arity 方法:
- 对于 1 = i < n,ei 的类型 Ai 可以通过方法调用转换为 Si。
- ...
(其他条件是指此处不相关的调用形式,例如真正使用 varargs 的调用,或涉及泛型的调用)
参考示例:当可以通过方法调用转换转换为相应的形式化方法参数时,方法适用于类型的实际参数表达式。根据JLS7中有关方法调用转换的相应部分,允许以下转换:b
Byte
b
- 身份转换 (§5.1.1)
- 加宽基元转换 (§5.1.2)
- 加宽参考转换 (§5.1.5)
- 装箱转换 (§5.1.7),然后可选择加宽参考转换
- 取消装箱转换 (§5.1.8),然后选择性地进行加宽基元转换。
显然,根据此规范,有两种方法适用:
-
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:识别匹配 Arity 方法适用于子类型开始。然后,该方法已经适用于该参数,因为它是 的子类型。没有理由进入阶段2:识别方法调用转换适用的匹配Arity方法。在此阶段中,将通过从 到 取消装箱的方法调用转换将适用,但从未达到此阶段。m(Number b)
Byte b
Byte
Number
Byte
byte