我有两个比较器,我使用
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 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()));
演员表。您的其他代码片段确实有效的原因是局部变量类型充当“类型提示”。