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

时间:2019-04-15 16:57:24

标签: 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);
    }
}

如代码注释所示,max()方法应传递类型为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);
    }
}

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

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

4 个答案:

答案 0 :(得分:19)

var chartOptions = {
    xAxis: [{
        type: 'value'
    }],
    yAxis: [{
        type: 'value'
    }],
    series: [{
        type: "line",
        data: [[0, 4], [1, 3], [2, 2], [3, 2], [4, 1], [5, 2]],
    }, {
        type: "line",
        data: [[0, 2], [1, 3], [2, 4], [3, 8], [4, 5], [5, 0]],
    }],
};
var aChart = echarts.init(document.getElementById('aGraph'));
aChart.setOption(chartOptions);

以上代码段在逻辑上等同于以下内容:

int value = intList.stream().max(Integer::compareTo).get();

从逻辑上讲,这还等同于以下内容:

int value = intList.stream().max((a, b) -> a.compareTo(b)).get();

int value = intList.stream().max(new Comparator<Integer>() { @Override public int compare(Integer a, Integer b) { return a.compareTo(b); } }).get(); 是一个功能性接口,可以用作lambda或方法引用,这就是代码可以成功编译和执行的原因。

我建议阅读Oracle's tutorial on Method References(他们使用一个比较两个对象的示例)以及§15.13. Method Reference Expressions上的Java语言规范来了解其工作原理。

答案 1 :(得分:13)

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

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

int compare(T o1, T o2);

我们有一个Integer的方法,它带有一个参数

int compareTo(Integer anotherInteger)

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

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

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

  

第二,给定具有n参数的目标函数类型,确定一组可能适用的方法:

     

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

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

  •   
  • 要搜索的类型的成员方法,该成员方法可能适用于名称为Identifier,arity为n-1,类型参数为TypeArguments且出现在相同位置的方法调用类作为方法引用表达式

  •   
     

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

...

     

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

     

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

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

(implicitParam, anotherInteger) -> implicitParam.compareTo(anotherInteger)

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

  

Integer::compareTo实现Comparable接口,而不是Comparator

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

Comparator<Integer> a = Integer::compareTo;
BiFunction<Integer, Integer, Integer> b = Integer::compareTo;
ToIntBiFunction<Integer, Integer> c = Integer::compareTo;

答案 2 :(得分:7)

@user通过覆盖Integer来实现Comparable

但是,可以用满足并实现compareTo接口的方式来使用覆盖的compareTo

此处用法

Comparator

它被翻译成类似的东西

int value = intList.stream().max(Integer::compareTo).get();

方法引用(或lambda表达式)必须满足相应功能接口的单个​​抽象方法的签名,在这种情况下(int value = intList.stream().max(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); } }).get(); ),Comparator必须满足。


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

({Integer还会返回compareTo要求的int。)

答案 3 :(得分:1)

第一个窍门:所有 instance方法实际上都接受了另外一个隐式参数,您在方法主体中将其称为this。例如:

public final class Integer extends Number implements Comparable<Integer> {
    public int compareTo(/* Integer this, */ Integer anotherInteger) {
        return compare(this.value, anotherInteger.value);
    }
}

Integer a = 10, b = 100;
int compareResult            = a.compareTo(b);
// this actually 'compiles' to Integer#compareTo(this = a, anotherInteger = b)

第二招:如果参数的数量和类型(包括{{1},则Java编译器可以“转换”对某些功能接口的方法引用的签名。 })满足:

this

如您所见,interface MyInterface { int foo(Integer bar, Integer baz); } Integer a = 100, b = 1000; int result1 = ((Comparator<Integer>) Integer::compareTo).compare(a, b); int result2 = ((BiFunction<Integer, Integer, Integer>) Integer::compareTo).apply(a, b); int result3 = ((MyInterface) Integer::compareTo).foo(a, b); // result1 == result2 == result3 并未实现class IntegerComparator或随机BiFunction的任何一种,但这并不会阻止您转换MyInterface方法参考作为那些接口。