为什么 Comparator.thenComparing() 在将比较器存储在变量中时起作用,但在内联它们时不起作用?

问题描述 投票:0回答:1

我有两个比较器,我使用

thenCompare()
:

链接起来
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.comaring()
,代码应该是等效的,但这也不起作用:

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 generics type-inference
1个回答
0
投票

Java lambda (

(arg1, arg2) -> code;
) 的类型推断有些奇怪。推理由内而外,然后由外而内,然后再次由内而外。

原因是java的函数式类型系统基于所谓的函数式接口。在 Java 中,您不只有

(int, int) -> String
类型的表达式。

这解释了为什么在java中你可以写

listOfStrings.sort(Comparator.comparing(x -> x.length()));
。因为,想一下这句话:java 是如何允许你在那里写
x.length()
的? j.l.Object 没有
length()
。为什么会
x
?我们从未提及
x
是什么类型。

那是因为 java 首先从内到外解析:好的,我们有一个 lambda。它将代表某种类型,但我们还不知道是什么。现在我们知道:它是一个 lambda,并且需要 1 个参数。 然后

javac

扫描 Comparator.comparing 并发现一些重载。它可以立即消除一些。最终只剩下一个:需要一个

Comparator<T>
。好的,所以我想现在我们选择
T t -> t.length()
。这没什么帮助。然而。我们继续前进:通过
.sort
传递给
List<String>
。啊哈!只有
Comparator<String>
在那里有效,所以我猜 T 是字符串!因此,让我们应用它 - 我们选择
String t -> t.length()
。最后,这“适合”。因此,java 将整个事情编译为 'a
x -> x.length()
。然后重新扫描该 lambda 以确保它在该上下文中有意义。只有现在才能例如
Comparator<String>
中的拼写错误为“嘿,这不是字符串所具有的方法”。
无法编译的代码片段的问题在于,编译器在执行此由内而外、由外而内的过程方面受到限制。它必须走得太远并放弃。 (它比这复杂得多,但是,您需要完全理解 JLS 的大片区域才能更进一步)。
添加一些强制转换来“帮助”编译器,它会再次工作:

length()

或添加

Comparator<Edge> myComparator = Comparator.comparing(e -> e.a.x()).thenComparing(Comparator.<Edge>comparing(e -> e.b.x()));
演员表。您的其他代码片段确实有效的原因是局部变量类型充当“类型提示”。


© www.soinside.com 2019 - 2024. All rights reserved.