为什么lambda表达式可以用作比较器?

Why can a lambda expression be used as a Comparator?

OCP 学习指南 一书中有一个关于 Comparator 的例子,它可以用两种方式初始化。第一个是通过这样的匿名 class:

Comparator<Duck> byWeight = new Comparator<Duck>(){
    public int compare(Duck d1, Duck d2){
        return d1.getWeight() - d2.getWeight();
    }
};

这个我能理解。根据这本书,这可以用这样的 lambda 表达式代替:

Comparator<Duck> byWeight = (d1,d2) -> d1.getWeight() - d2.getWeight();

现在这个我不明白。 lambda 表达式不是 return Comparator 对象,现在我认为它不能,因为 Comparator 是一个接口。

第一个示例中的 new 运算符是否引用正在创建的匿名 class,它被称为 Comparator,因为匿名 class 实现了 Comparator 接口?

那么示例 2 到底发生了什么?对象是以某种方式从 lambda 表达式中创建出来的吗?在此示例中,您使用 byWeight 作为参考变量对吗?

我真的不明白,谁能解释一下?谢谢。

在第一种方式中,您创建了一个新的匿名 class(它有一个带有行为的方法)。

在第二种方式中,您只是公开行为(将其视为一种共享函数、方法的方式,即使它是透明创建的,也不会看到周围的环境 class)。

我记得在Java Tutorial - The Lambda Expressions

里面解释的很清楚

One issue with anonymous classes is that if the implementation of your anonymous class is very simple, such as an interface that contains only one method, then the syntax of anonymous classes may seem unwieldy and unclear. In these cases, you're usually trying to pass functionality as an argument to another method, such as what action should be taken when someone clicks a button. Lambda expressions enable you to do this, to treat functionality as method argument, or code as data.

我建议您集中注意,使用 lambda 表达式,您只是试图向某些 class 或组件公开一种行为,您的示例可能是 Collections.sort

lambda 表达式让您有机会获得更清晰、更简单的表达式,避免匿名 class 声明的样板。

在Java8中Comparator<T>被注释为@FunctionalInterface。它的文档说:

An informative annotation type used to indicate that an interface type declaration is intended to be a functional interface as defined by the Java Language Specification. Conceptually, a functional interface has exactly one abstract method. Since default methods have an implementation, they are not abstract. If an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also does not count toward the interface's abstract method count since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.

Note that instances of functional interfaces can be created with lambda expressions, method references, or constructor references.

If a type is annotated with this annotation type, compilers are required to generate an error message unless:

The type is an interface type and not an annotation type, enum, or class. The annotated type satisfies the requirements of a functional interface. However, the compiler will treat any interface meeting the definition of a functional interface as a functional interface regardless of whether or not a FunctionalInterface annotation is present on the interface declaration.

这里最重要的部分是 instances of functional interfaces can be created with lambda expressions, method references, or constructor references.,它回答了您的问题。

在第一个代码块中创建的对象实例实现了Comparator<Duck>,但是对应的class没有名字(是匿名的)。

在第二个代码块中,发生了同样的事情。因为 Comparator 接口只定义了一个方法(名为 compare),所以可以使用 lambda 表达式简化接口的(匿名)实现的创建。

变量byWeight可以在两个例子中以相同的方式使用。在任何需要 Comparator<Duck> 的地方,都可以使用 byWeight - 这对应于变量的类型信息。在内部,每当在此实现上调用 compare 时,都会使用使用 lambda 表达式提供的定义。

如果你阅读了documentation of the Comparator interface,你可以阅读:

Functional Interface: This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

Comparator<T> 接口因此是 implemented like:

<b>@FunctionalInterface</b>
public interface Comparator<T> {

    int compare(T o1, T o2);

    // ...

}

现在,如果我们查看 @FunctionalInterface 的文档,我们会看到:

An informative annotation type used to indicate that an interface type declaration is intended to be a functional interface as defined by the Java Language Specification. Conceptually, a functional interface has exactly one abstract method. Since default methods have an implementation, they are not abstract. If an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also does not count toward the interface's abstract method count since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.

所以基本上,如果你有一个带有 one 抽象方法的接口,并且你将接口注释为 @FunctionalInterface,那么该接口就是函数的目标:在您或多或少构造了一个实现功能接口的匿名 class,您指定的函数是唯一抽象方法的实现。

换句话说,表达式:

Comparator<Duck> byWeight = <somelambda>

等同于

Comparator<Duck> byWeight = new Comparator<Duck>(){
    public int compare(Duck d1, Duck d2){
        return <somelambda>(d1,d2);
    }
}

基本上 lambda 的行为与内部 类 非常相似,但允许以更少的开销使用更好的语法。

With Java8 Comparator 是一个 @FunctionalInterface (JavaDoc),这使得它可以与 lambda 表达式一起使用。也就是说,您可以使用 return 类型 int 定义一个双函数(具有两个参数的函数)并将其用作 Comparator,这在您的第二个示例中完成。 Comparator 两个实例都可以在您的剩余代码中以完全相同的方式使用。

比较器基本上只是一个带有两个参数和 returns 一个整数的函数。

实际上,这里发生的事情是编译器能够根据您声明左侧的方式巧妙地推断右侧需要的内容。

Comparator<Duck> byWeight = (d1,d2) -> d1.getWeight() - d2.getWeight();
                           //^   ^ I know a Comparator<Duck> takes two Ducks.
                                      // ^ I know a Comparator<Duck> returns an int

这一切都是可能的,因为 Comparator<T> is defined as a :

This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

Lambda 表达式只是 函数式接口的简写(只有一个函数的接口),你不需要需要写 new/function name 只需在 ( yourParameterListHere ) 中写参数列表,然后 -> 然后写什么到 do/return(即函数体)。你也可以写成 { } like

Comparator<Duck> byWeight = (d1,d2) -> { d1.getWeight() - d2.getWeight(); }

接口Comparator是一个函数式接口,这意味着这个接口只能包含一个抽象方法。

然后就可以使用lambda表达式来定义这个抽象方法的实现了,基本上(d1,d2) -> d1.getWeight() - d2.getWeight();就是抽象方法int compare(T o1, T o2);的实现。

由于函数式接口只包含一个抽象方法,您可以使用lambda表达式来定义此类接口的实现

您可以将 lambda 视为不属于 class 的方法,它可以像数据一样传递。

或者,通过非常小的心理转变,您可以将其视为只有一种方法的对象。

有一组接口标记为功能接口。这在 Javadoc 中提到为:

Functional Interface:

This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

这是一种技术表达方式,因为它们只有一个方法,并且被标记为 功能性,编译器可以将 lambda 视为具有该接口的对象。它知道使用 lambda 作为接口中一个方法的实现。

所以你可以这样做:

Comparator<Pet> byNameComparator = (f1,f2) -> f1.name().compareTo(f2.name());
Predicate<Customer> isVip = cust -> cust.orderCount > 20;
Callable<Item> getResult = () -> queue.getItem();
Function<Integer,Integer> multiply = (a,b) -> a * b;

...等等。

只要参数的类型是函数式接口,您就可以直接使用 lambda,因此给出:

 public void sort(Comparator cmp);

...你可以这样称呼它:

 foo.sort( (a,b) -> a.name().compareTo(b.name()));