传递重载方法引用给重载方法时Java编译器出现混淆问题
Java重载方法引用传递给重载方法时的编译行为疑惑
以下是测试代码:
public class OverloadedMethodReferenceToOverloadedMethod<X, Y> { public void method(Function<X, ?> f) {} public void method(Supplier<?> s) {} public void methodFunction(Function<X, ?> f) {} public void methodSupplier(Supplier<?> s) {} class NonMatchingOverload { public NonMatchingOverload() {} public NonMatchingOverload(Y overload) {} } class MatchingOverload { public MatchingOverload() {} public MatchingOverload(X overload) {} } public void foo() { // (1) 编译失败:编译器报错参数列表长度不匹配 methodFunction(Object::new); // (2) 编译失败:重载方法的Y类型参数与Function期望的X类型不匹配 methodFunction(NonMatchingOverload::new); // (3) 编译成功:编译器正确匹配重载构造方法到Function methodFunction(MatchingOverload::new); // (4) 编译成功:可将无参构造方法作为Supplier传递 methodSupplier(Object::new); methodSupplier(NonMatchingOverload::new); methodSupplier(MatchingOverload::new); // (5) 编译成功:编译器将Object唯一的无参构造方法匹配到Supplier重载 method(Object::new); // (6) 编译失败(符合预期):两个重载均匹配构造方法,引用存在歧义 method(MatchingOverload::new); // (7) 编译失败(疑似异常):编译器同时认为这是“歧义引用”和“无效构造方法引用” // 认为歧义是因为它觉得Function和Supplier重载均匹配(如前例) // 认为无效是因为无法将带参构造的Y类型参数转换为Function期望的X类型 method(NonMatchingOverload::new); // (8) 编译成功:显式将构造方法引用指定为Supplier类型时,编译器可匹配正确重载 Supplier<NonMatchingOverload> sup = NonMatchingOverload::new; method(sup); method((Supplier<NonMatchingOverload>) NonMatchingOverload::new); } }
核心疑问解答
1. 语句#7:为何一个选项无效仍判定歧义?
Java编译器的重载解析分为两个阶段:候选方法收集和适用性检查。
- 候选方法收集阶段:编译器会先找出所有名称匹配、参数数量/类型初步兼容的方法。对于
method(NonMatchingOverload::new),两个method重载都会被纳入候选:method(Supplier<?>):无参构造方法符合Supplier的无参要求,参数数量匹配;method(Function<X, ?>):带Y参数的构造方法参数数量为1,和Function的apply方法参数数量匹配,因此也会被纳入候选。
- 适用性检查阶段:编译器才会检查类型兼容性。此时发现
NonMatchingOverload(Y)无法适配Function<X, ?>(Y和X无约束关系,无法转换),但候选阶段已经存在两个方法,编译器无法确定你要调用哪一个,因此优先报歧义错误,同时在检查Function适配时会附带类型不兼容的错误。
编译器不会因为其中一个候选最终不适用就自动排除它,歧义判定发生在适用性检查之前,这是Java重载解析的规则。
2. 语句#2:为何报错类型不匹配而非参数长度问题?
methodFunction的目标接口是Function<X, ?>,要求方法引用必须对应带1个参数的方法/构造:
NonMatchingOverload的无参构造参数数量为0,不符合Function的参数要求,直接被排除;- 带Y参数的构造参数数量为1,符合参数数量要求,因此编译器尝试匹配它,随后检查类型时发现Y无法转换为X,因此报错类型不匹配,而非参数长度问题。
这和语句#7的根源一致:编译器优先匹配参数数量符合的候选,再做类型兼容性检查,不会因为类型不兼容就回溯到参数数量不匹配的选项。
附加问题:测试类命名建议
可以使用更具描述性的名称,比如:
OverloadResolutionAmbiguityWithMethodReferencesMethodReferenceToOverloadedTargetMethodsTestOverloadedCtorReferenceVsOverloadedFunctionalInterfaceTest
内容的提问来源于stack exchange,提问作者superbadcodemonkey
相关产品推荐
相关产品推荐

