为何Comparator.thenComparing()变量存储可行,内联则编译报错?
我有两个Comparator,使用thenComparing()进行链式调用:
Comparator<Edge> byAX = Comparator.comparing(e -> e.a.x()); Comparator<Edge> byBX = Comparator.comparing(e -> e.b.x()); Comparator<Edge> myComparator = byAX.thenComparing(byBX);
这段代码运行完全正常。
但如果将代码内联,替换byAX和byBX的定义后,代码无法编译:
Comparator<Edge> myComparator = Comparator.comparing(e -> e.a.x()).thenComparing(Comparator.comparing(e -> e.b.x()));
我认为可以省略第二个Comparator.comparing(),代码应该等价,但同样无法运行:
Comparator<Edge> myComparator = Comparator.comparing(e -> e.a.x()).thenComparing(e -> e.b.x());
得到如下编译错误:
java: cannot find symbol symbol: variable a location: variable e of type java.lang.Object java: cannot find symbol symbol: variable b location: variable e of type java.lang.Object java: incompatible types: no instance(s) of type variable(s) U exist so that java.util.Comparator<java.lang.Object> conforms to java.util.Comparator<CompGeom.Edge>
从错误信息推测这与泛型及类型推断有关,但我不清楚具体原因,恳请解释。
这个问题核心是Java泛型类型推断的上下文限制:
分步定义时的类型推断逻辑
当你把byAX和byBX单独定义为Comparator<Edge>变量时,编译器能直接从变量的显式类型,推断出Comparator.comparing()的泛型输入参数是Edge。此时lambda表达式里的e会被明确识别为Edge类型,自然能正常访问e.a.x()或e.b.x();后续thenComparing(byBX)也能匹配Comparator<Edge>的链式调用要求,所以代码能正常编译。内联时的类型推断失效原因
把代码内联后,编译器处理Comparator.comparing(e -> e.a.x())时,没有足够的上下文信息来推断e的类型。因为此时comparing()的返回值还未与后续的thenComparing()或最终的Comparator<Edge>变量建立关联,编译器只能默认将e推断为Object类型——而Object类没有a、b属性,因此会报“找不到符号”的错误,后续的泛型类型不匹配错误也是由此衍生而来。两种可行的解决方式
- 显式指定泛型参数:给
comparing()明确声明输入类型和返回值类型,强制编译器识别lambda参数类型:
// 假设x()方法返回Integer,可根据实际返回类型调整第二个泛型参数 Comparator<Edge> myComparator = Comparator.<Edge, Integer>comparing(e -> e.a.x()) .thenComparing(e -> e.b.x());
- 给lambda参数显式声明类型:直接在lambda表达式里指定
e的类型为Edge,消除推断歧义:
Comparator<Edge> myComparator = Comparator.comparing((Edge e) -> e.a.x()) .thenComparing((Edge e) -> e.b.x());
通过以上任意一种方式,编译器都能正确识别参数类型,链式调用即可正常编译运行。
内容的提问来源于stack exchange,提问作者Aaronmacaron

