Java 8 流的 .min() 和 .max(): 为什么会编译?

2022-08-31 05:58:51

注意:这个问题源于一个死链接,这是以前的SO问题,但这里...

请参阅此代码(注意:我确实知道此代码不会“工作”,并且应该使用Integer::compare - 我刚刚从链接的问题中提取了它):

final ArrayList <Integer> list 
    = IntStream.rangeClosed(1, 20).boxed().collect(Collectors.toList());

System.out.println(list.stream().max(Integer::max).get());
System.out.println(list.stream().min(Integer::min).get());

根据 .min().max() 的 javadoc,两者的参数都应该是 .然而,这里的方法引用是Integer类的静态方法。Comparator

那么,为什么会编译呢?


答案 1

让我解释一下这里发生了什么,因为它并不明显!

首先,Stream.max() 接受比较器的实例,以便可以对流中的项目进行比较,以找到最小值或最大值,以某种最佳顺序,您无需过多担心。

所以问题是,当然,为什么整数::max被接受?毕竟它不是一个比较器!

答案在于新的 lambda 功能在 Java 8 中的工作方式。它依赖于一个概念,该概念被非正式地称为“单抽象方法”接口或“SAM”接口。这个想法是,任何具有一个抽象方法的接口都可以由任何lambda或方法引用自动实现,其方法签名与接口上的一个方法匹配。因此,检查比较器接口(简单版本):

public Comparator<T> {
    T compare(T o1, T o2);
}

如果某个方法正在寻找 ,则它实质上是查找此签名:Comparator<Integer>

int xxx(Integer o1, Integer o2);

我使用“xxx”,因为方法名称不用于匹配目的

因此,两者都足够接近,自动装箱将允许它作为方法上下文中显示。Integer.min(int a, int b)Integer.max(int a, int b)Comparator<Integer>


答案 2

Comparator是一个功能接口,并且符合该接口(在考虑自动装箱/取消装箱之后)。它需要两个值并返回一个 - 就像你期望的那样(再次,眯眼忽略整数/整数差异)。Integer::maxintintComparator<Integer>

但是,我不期望它做正确的事情,因为这不符合 的语义。事实上,它一般并不真正有效。例如,进行一个小的更改:Integer.maxComparator.compare

for (int i = 1; i <= 20; i++)
    list.add(-i);

...现在值为 -20,值为 -1。maxmin

相反,两个调用都应使用:Integer::compare

System.out.println(list.stream().max(Integer::compare).get());
System.out.println(list.stream().min(Integer::compare).get());

推荐