为什么仅一个重载合法时,这个Java泛型方法调用仍被判定为歧义?
Java泛型方法重载歧义问题分析
问题描述
我想弄清楚为什么编译器无法解析bar方法调用。我原本预期bar(Xyz::new)会始终匹配bar(Supplier)重载,因为bar(T extends Xyz)受Xyz的上界限制,永远不可能满足匹配条件。
相关代码如下:
public <T extends Xyz> void foo(T s) {} public <T extends Xyz> void bar(T s) {} public <T extends Xyz> void bar(Supplier<T> s) {} public void example() { foo(Xyz::new); // 编译不通过:方法引用的类型不满足T extends Xyz的约束 bar((Supplier<Xyz>) Xyz::new); // 编译通过:显式指定参数是Supplier类型 bar(Xyz::new); // 编译器报歧义:但理论上只有Supplier重载是合法的? } public static class Xyz {}
既然bar(T)就算单独存在也无法适配Xyz::new的调用(和foo方法的报错逻辑一致),那为什么编译器不直接选择bar(Supplier)重载,反而判定为调用歧义?
问题原因
这个现象是Java语言规范规定的重载解析执行顺序导致的:
- 重载解析的早期筛选阶段,只会判断方法参数的结构适配性,不会提前做泛型上界校验、方法引用类型合法性校验。对于
bar(T s)这个泛型方法,编译器在筛选候选时只会识别到参数是一个未绑定的类型变量,方法引用作为参数在结构上存在适配可能性,因此会把bar(T s)纳入候选列表。 - 两个候选方法
bar(T s)和bar(Supplier<T> s)之间不存在“某一个比另一个更具体”的优先级关系,编译器在这个阶段就会直接抛出歧义错误,根本不会走到后续的类型校验阶段,自然也不会提前发现bar(T s)其实最终无法满足类型约束。 - 你看到的
foo(Xyz::new)的编译报错,是候选方法唯一确定后,做类型合法性校验时抛出的错误,执行顺序晚于重载候选筛选阶段。
当你给Xyz::new加上Supplier<Xyz>的强制类型转换后,参数类型在重载筛选阶段就已经明确,只会匹配bar(Supplier<T>)重载,自然不会产生歧义。
内容的提问来源于stack exchange,提问作者Timothy Cole
相关产品推荐
相关产品推荐

