Java 8stream的.min()和.max():为什么编译?
注:这个问题来自一个死链接,这是一个以前的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,两者的参数应该是Comparator
。 然而这里的方法引用是Integer
类的静态方法。
那么,为什么这个编译呢?
让我来解释一下这里发生的事情,因为这并不明显!
首先, Stream.max()
接受Comparator
一个实例,以便stream中的项目可以相互比较以find最小值或最大值,按照某种最佳顺序,您不必过多担心。
所以问题是,为什么Integer::max
被接受? 毕竟这不是一个比较!
答案就在于,新的lambdafunction在Java 8中起作用。它依赖于非正式地被称为“单抽象方法”接口或“SAM”接口的概念。 我们的想法是,任何具有抽象方法的接口都可以通过任何lambda或方法引用自动实现,其方法签名与接口上的一个方法相匹配。 因此,检查Comparator
接口(简单版本):
public Comparator<T> { int 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>
。
Comparator
是一个function接口 , Integer::max
符合该接口(考虑到自动装箱/取消装箱)。 它需要两个int
值并返回一个int
– 就像你期望的Comparator<Integer>
(再次,斜视忽略Integer / int的差异)。
但是,我不希望它做正确的事情,因为Integer.max
不符合Comparator.compare
的语义 。 事实上它并不是一般的工作。 例如,做一个小小的改变:
for (int i = 1; i <= 20; i++) list.add(-i);
…现在max
是-20, min
是-1。
相反,这两个调用应该使用Integer::compare
:
System.out.println(list.stream().max(Integer::compare).get()); System.out.println(list.stream().min(Integer::compare).get());
这是因为Integer::min
parsing为Comparator<Integer>
接口的实现。
Integer::min
的方法引用parsing为Integer.min(int a, int b)
,parsing为IntBinaryOperator
,推测自动装箱发生在某个地方使其成为BinaryOperator<Integer>
。
Stream<Integer>
的min()
resp max()
方法要求实现Comparator<Integer>
接口。
现在,这解决了单个方法Integer compareTo(Integer o1, Integer o2)
。 哪个types是BinaryOperator<Integer>
。
因此,这两个方法都是BinaryOperator<Integer>
。
除了David M. Lloyd给出的信息之外,可以补充说的是允许这个的机制被称为目标types 。
这个想法是,编译器赋给一个lambdaexpression式或一个方法引用的types不仅仅取决于expression式本身,还取决于它的使用位置。
expression式的目标是分配其结果的variables或其结果被传递给的参数。
如果可以find这种types,则可以为Lambdaexpression式和方法引用分配一个与其目标types相匹配的types。
有关更多信息,请参阅Java教程中的Type Inference部分 。