Java 8流max()函数参数类型Comparator与Comparable

Sha*_*ika 17 java java-8 java-stream

我写了一些简单的代码,如下所示。此类正常运行,没有任何错误。

public class Test {
    public static void main(String[] args) {
        List<Integer> intList = IntStream.of(1,2,3,4,5,6,7,8,9,10).boxed().collect(Collectors.toList());
        int value = intList.stream().max(Integer::compareTo).get();

        //int value = intList.stream().max(<Comparator<? super T> comparator type should pass here>).get();

        System.out.println("value :"+value);
    }
}
Run Code Online (Sandbox Code Playgroud)

如代码注释所示,该max()方法应传递type参数Comparator<? super Integer>

但是Integer::compareTo实现Comparable接口- 不是 Comparator

public final class Integer extends Number implements Comparable<Integer> {
    public int compareTo(Integer anotherInteger) {
        return compare(this.value, anotherInteger.value);
    }
}
Run Code Online (Sandbox Code Playgroud)

这怎么工作?该max()方法说它需要一个Comparator参数,但是可以与Comparable参数一起使用。

我知道我误会了一些东西,但是现在我知道了。有人可以解释一下吗?

Jac*_* G. 20

int value = intList.stream().max(Integer::compareTo).get();
Run Code Online (Sandbox Code Playgroud)

上面的代码片段在逻辑上等效于以下内容:

int value = intList.stream().max((a, b) -> a.compareTo(b)).get();
Run Code Online (Sandbox Code Playgroud)

从逻辑上讲,这也等效于以下内容:

int value = intList.stream().max(new Comparator<Integer>() {
    @Override
    public int compare(Integer a, Integer b) {
        return a.compareTo(b);
    }
}).get();
Run Code Online (Sandbox Code Playgroud)

Comparator 是一个功能接口,可以用作lambda或方法引用,这就是为什么代码可以成功编译和执行的原因。

我建议阅读有关方法参考的Oracle教程(它们使用一个比较两个对象的示例)以及第15.13节中的Java语言规范方法参考表达式以了解其工作原理。

  • 尽管这是绝对正确的,但它没有回答“这怎么工作?” (9认同)

And*_*lko 13

我可以和你的困惑联系起来。

我们有一个Comparator声明两个参数的方法

int compare(T o1, T o2);
Run Code Online (Sandbox Code Playgroud)

我们有一个Integer采用一个参数的方法

int compareTo(Integer anotherInteger)
Run Code Online (Sandbox Code Playgroud)

到底如何Integer::compareTo解决Comparator实例?

当方法引用指向实例方法时,解析器可以查找具有稀疏性n-1n期望的参数数量)的方法。

这是JLS的摘录,内容涉及如何确定适用的方法。我将删除关于分析::令牌前面的表达式的第一部分。

其次,给定具有n参数的目标功能类型,确定一组可能适用的方法:

如果方法引用表达式的格式为ReferenceType :: [TypeArguments] Identifier,则可能适用的方法是:

  • 可能适用的搜索类型的成员方法(第15.12.2.1节),该方法调用的名称为Identifier,具有Arity n,类型参数为TypeArguments,并且与方法引用表达式位于同一类中;加

  • 搜索类型的成员方法,该方法可能适用于名称Identifier为,名称为Ar-1,类型参数为TypeArguments,并与方法引用表达式位于同一类中的方法调用。

考虑了两种不同的ar nn-1,以说明这种形式引用静态方法还是实例方法的可能性。

...

表单的方法引用表达式ReferenceType :: [TypeArguments] Identifier可以用不同的方式解释。如果Identifier引用实例方法,则隐式lambda表达式Identifier引用静态方法相比具有额外的参数

https://docs.oracle.com/javase/specs/jls/se12/html/jls-15.html#jls-15.13.1

如果要从该方法引用中编写一个隐式的lambda表达式,则第一个(隐式)参数将是调用该方法的实例,第二个(显式)参数将是传递给该方法的参数。

(implicitParam, anotherInteger) -> implicitParam.compareTo(anotherInteger)
Run Code Online (Sandbox Code Playgroud)

请注意,即使前者可以轻松转换为后者,方法引用也不同于lambda表达式。Lambda表达式需要分解为新方法,而方法引用通常仅需要加载相应的常量方法句柄。

Integer::compareTo实现Comparableinterface-not Comparator

Integer::compareTo因为表达式不实现任何接口。但是,它可以引用/表示不同的功能类型,其中之一是Comparator<Integer>

Comparator<Integer> a = Integer::compareTo;
BiFunction<Integer, Integer, Integer> b = Integer::compareTo;
ToIntBiFunction<Integer, Integer> c = Integer::compareTo;
Run Code Online (Sandbox Code Playgroud)


Sav*_*ior 7

IntegerComparable通过覆盖实现compareTo

compareTo但是,可以以满足和实现Comparator接口的方式使用该替代。

在这里使用

int value = intList.stream().max(Integer::compareTo).get();
Run Code Online (Sandbox Code Playgroud)

它被翻译成类似

int value = intList.stream().max(new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o1.compareTo(o2);
    }
}).get();
Run Code Online (Sandbox Code Playgroud)

方法引用(或lambda表达式)必须满足相应功能接口的单个​​抽象方法的签名,在这种情况下(Comparator),必须compareTo这样做。


这个想法是max期望a Comparator及其compare方法期望两个Integer对象。Integer::compareTo可以满足这些期望,因为它也期望有两个Integer对象。第一个是它的接收者(在其上调用方法的实例),第二个是参数。使用新的Java 8语法,编译器可以将一种样式转换为另一种样式。

compareTo还返回int所要求的Comparator#compare。)