JDK17静态上下文调用重载方法为何不优先匹配可变参数静态方法?
这不是JDK 17的编译器Bug,反而是JDK 17修复了此前JDK 8、JDK 11版本中不符合Java语言规范的重载解析逻辑偏差,现在的报错是严格遵循JLS规则的正确表现。
Java的方法重载解析是分阶段按优先级筛选候选方法的,静态/非静态的访问合法性校验,发生在最匹配方法签名确定之后,而非筛选候选的阶段,三个匹配阶段的优先级从高到低为:
- 第一阶段:不允许装箱/拆箱、不允许可变参数,仅匹配固定参数签名完全吻合的方法
- 第二阶段:允许装箱/拆箱、不允许可变参数,匹配参数类型适配的方法
- 第三阶段:允许装箱/拆箱、允许可变参数,匹配包括可变参数签名在内的所有适配方法
你的代码中调用doSomething(1, 2)时,传入两个int类型实参,第一阶段筛选就直接命中了固定参数签名doSomething(int, int)——这个固定签名的匹配优先级远高于可变参数版本的doSomething(int...),哪怕前者是实例方法,也会被优先选为最匹配的候选方法。
等候选方法确定后,编译器才会做上下文合法性校验:当前调用发生在静态的main方法中,没有类实例引用,无法调用非静态的实例方法,因此抛出Cannot make a static reference to the non-static method doSomething(int, int)的编译错误。
JDK 8、JDK 11的编译器实现存在逻辑偏差:在重载解析的第一阶段筛选时,错误地将静态上下文下无法直接调用的非静态方法直接排除出了候选列表。这种情况下第一阶段找不到匹配的固定参数方法,流程会走到第三阶段,最终匹配到静态的可变参数版本doSomething(int...),因此不会报错。这个行为不符合Java语言规范的明文要求,JDK 17版本对该逻辑做了修正,对齐了规范要求。
你可以通过两种修改方式验证这个逻辑:
- 将非静态的
doSomething(int, int)加上static修饰,编译运行后会输出2,证明固定参数签名的优先级确实更高 - 将
main方法中的调用改为传3个int值,比如doSomething(1,2,3),第一阶段找不到匹配的固定参数方法,会自动匹配到静态可变参数版本,编译正常,运行输出1
内容的提问来源于stack exchange,提问作者anupamD

