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

fge*_*fge 213 java java-8 java-stream

注意:这个问题源于一个死链接,这是以前的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());
Run Code Online (Sandbox Code Playgroud)

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

那么,为什么要编译呢?

Dav*_*oyd 242

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

首先,Stream.max()接受一个实例,Comparator以便可以将流中的项目相互比较以找到最小值或最大值,以一些您不需要过多担心的最佳顺序.

所以问题当然是为什么被Integer::max接受了?毕竟它不是比较器!

答案就是新的lambda功能在Java 8中的工作方式.它依赖于一种非正式地称为"单一抽象方法"接口或"SAM"接口的概念.这个想法是任何带有一个抽象方法的接口都可以通过任何lambda或方法引用自动实现 - 其方法签名与接口上的一个方法匹配.所以检查Comparator界面(简单版本):

public Comparator<T> {
    T compare(T o1, T o2);
}
Run Code Online (Sandbox Code Playgroud)

如果一个方法正在寻找a Comparator<Integer>,那么它基本上是在寻找这个签名:

int xxx(Integer o1, Integer o2);
Run Code Online (Sandbox Code Playgroud)

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

因此,无论是Integer.min(int a, int b)Integer.max(int a, int b)是足够接近,自动装箱会允许这种情况出现的Comparator<Integer>一个方法上下文.

  • 或者:`list.stream().mapToInt(i - > i).max().get()`. (28认同)
  • @assylias你想使用`.getAsInt()`代替`get()`,因为你正在处理`OptionalInt`. (13认同)
  • @ChrisKerekes装饰器`@ FunctionalInterface`主要仅用于文档目的,因为编译器可以使用一个抽象方法在任何接口上愉快地执行此操作. (2认同)

Jon*_*eet 116

Comparator是一个功能界面,并Integer::max符合该界面(考虑自动装箱/拆箱后).它需要两个int值并返回int- 正如您所期望的那样Comparator<Integer>(再次,斜视忽略Integer/int差异).

不过,我不希望它做正确的事情,因为Integer.max它不符合语义Comparator.compare.事实上,它确实不起作用.例如,做一个小改动:

for (int i = 1; i <= 20; i++)
    list.add(-i);
Run Code Online (Sandbox Code Playgroud)

...现在max值为-20,min值为-1.

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

System.out.println(list.stream().max(Integer::compare).get());
System.out.println(list.stream().min(Integer::compare).get());
Run Code Online (Sandbox Code Playgroud)

  • 我认为潜在的问题是`Comparator.compare`的类型签名.它应该返回``LessThan,GreaterThan,Equal}`的`enum`,而不是`int`.这样,功能接口实际上不匹配,你会得到一个编译错误.IOW:`Comparator.compare`的类型签名没有充分捕获比较两个对象意味着什么的语义,因此与比较对象完全无关的其他接口意外地具有相同的类型签名. (19认同)
  • @fge:编译器是否应该知道`Integer :: max`的语义?从它的角度来看,你传递了一个符合其规范的函数,这就是它真正可以继续下去的. (7认同)
  • @fge:你不清楚拆箱吗?(我没有仔细研究那个部分.)`Comparator <Integer>`将有`int compare(Integer,Integer)`... Java允许方法引用`int max(int)并不令人难以置信,int)`转换为... (6认同)
  • @fge:特别是,如果你理解正在发生的事情的*部分*,但是对它的一个特定方面感兴趣,那么值得在问题中明确这一点,以避免人们浪费时间来解释你已经知道的部分. (6认同)

ski*_*iwi 19

这有效,因为Integer::min解析了Comparator<Integer>接口的实现.

Integer::min解决Integer.min(int a, int b),解决IntBinaryOperator和可能自动装箱的方法参考发生在使其成为的地方BinaryOperator<Integer>.

并且要求实现接口的min()resp max()方法. 现在这解决了单一方法.哪种类型.Stream<Integer>Comparator<Integer>
Integer compareTo(Integer o1, Integer o2)BinaryOperator<Integer>

因此,两种方法都是一种魔力BinaryOperator<Integer>.