首页 文章

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

提问于
浏览
193

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

请参阅此代码( note: I do know that this code won't "work" and that Integer::compare should be used -- I just extracted it from the linked question ):

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类的静态方法 .

那么,为什么要编译呢?

5 回答

  • 227

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

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

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

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

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

    如果某个方法正在寻找 Comparator<Integer> ,那么它实际上是在寻找这个签名:

    int xxx(Integer o1, Integer o2);
    

    我用"xxx" because the method name is not used for matching purposes .

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

  • 110

    Comparator 是一个功能接口, 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());
    
  • 1

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

    Integer::min 的方法参考解析为 Integer.min(int a, int b) ,解析为 IntBinaryOperator ,并且可能在某处使自动装箱成为 BinaryOperator<Integer> .

    并且 Stream<Integer>min() resp max() 方法要求实现 Comparator<Integer> 接口 .
    现在这解析为单个方法 Integer compareTo(Integer o1, Integer o2) . 哪个是 BinaryOperator<Integer> 类型 .

    因此,两种方法都是 BinaryOperator<Integer> ,因此发生了魔法 .

  • 2

    除了David M. Lloyd提供的信息之外,可以补充说,允许这种情况的机制称为目标类型 .

    我们的想法是编译器分配给lambda表达式或方法引用的类型不仅取决于表达式本身,还取决于它的使用位置 .

    表达式的目标是分配其结果的变量或其结果传递到的参数 .

    如果可以找到这样的类型,则为Lambda表达式和方法引用分配与其目标类型匹配的类型 .

    有关更多信息,请参阅Java教程中的Type Inference section .

  • 19

    我有一个错误,数组得到最大和最小,所以我的解决方案是:

    int max = Arrays.stream(arrayWithInts).max().getAsInt();
    int min = Arrays.stream(arrayWithInts).min().getAsInt();
    

相关问题