为什么这种方法重载不明确?

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)

我已经搜索过并发现扩展优先级高于取消装箱,因此在上面的方法调用中,应该调用第一个方法,因为第二个参数对于两者都是相同的.但这不会发生.你可以解释一下吗?

Mar*_*o13 7

它无法在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

  • 身份转换(§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阶段开始:识别匹配的Arty方法(Subtyping).m(Number b)然后该方法Byte b由于Byte是子类型而已经适用于该参数Number.没有理由进入阶段2:识别方法调用转换所适用的匹配Arity方法.在这个阶段,通过拆箱方法调用转换从Bytebyte会申请,但从来没有达到这个阶段.

  • 这里有类似的问题http://stackoverflow.com/questions/30130720/method-overload-ambiguity-with-java-8-ternary-conditional-and-unboxed-primitives/30137369#30137369 (2认同)
  • @ medvedev1088正如上面的回答中提到的:JLS的这部分可能是最复杂的一部分,并且在这方面"改进"JLS很困难.任何*简化*的尝试都可能为模糊性打开大门,另一方面,试图让它更清晰*并指出它目前所说的某些含义可能会使它变得更加复杂.(而且我们仍然在谈论"琐碎"的案例:**真正的**讨厌的部分只有在涉及泛型时才会发挥作用!).所以我不知道该怎么做.(顺便说一句:我用另一个链接更新了这个答案) (2认同)