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

时间:2017-05-09 09:25:10

标签: java lambda java-8 comparator anonymous-inner-class

OCP学习指南一书中,有一个关于可以用两种方式初始化的比较器的示例。第一个是通过这样的匿名类:

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表达式不返回Comparator对象,由于Comparator是一个接口,因此我现在无法想到它。

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

然后在例2中究竟发生了什么?是否以lambda表达式创建了某个对象?在此示例中,您使用byWeight作为参考变量吗?

我真的不明白这一点,有人可以解释一下吗?谢谢。

9 个答案:

答案 0 :(得分:11)

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

  

功能界面:       这是一个功能接口,因此可以用作lambda表达式或方法引用的赋值目标。

因此Comparator<T>界面为implemented like

@FunctionalInterface
public interface Comparator<T> {

    int compare(T o1, T o2);

    // ...

}

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

  

用于指示接口类型的信息性注释类型   声明是旨在成为由...定义的功能接口   Java语言规范。从概念上讲,功能界面具有   正是一种抽象方法。由于默认方法有   实施,它们不是抽象的。如果接口声明了   抽象方法覆盖其中一个公共方法   java.lang.Object,也不计入接口   抽象方法计数,因为任何接口的实现都会   有来自java.lang.Object或其他地方的实施。

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

换句话说,表达式:

Comparator<Duck> byWeight = <somelambda>

等同于

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

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:2)

在Java 8 Comparator<T>中注明了@FunctionalInterface。它的文档说:

  

用于指示接口类型的信息性注释类型   声明旨在成为由...定义的功能接口   Java语言规范。从概念上讲,功能界面具有   正是一种抽象方法。由于默认方法有   实施,它们不是抽象的。如果接口声明了   抽象方法覆盖其中一个公共方法   java.lang.Object,也不计入接口   抽象方法计数,因为任何接口的实现都会   有一个来自java.lang.Object或其他地方的实现。

     

请注意,可以使用创建功能接口的实例   lambda表达式,方法引用或构造函数引用。

     

如果使用此注释类型注释类型,则编译器是   需要生成错误消息,除非:

     

类型是接口类型,而不是注释类型,枚举或   类。带注释的类型满足功能的要求   接口。但是,编译器会处理任何符合的接口   将功能接口定义为功能接口   无论FunctionalInterface注释是否是   出现在界面声明中。

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

答案 3 :(得分:2)

比较器基本上只是一个函数,它接受两个参数并返回一个int。

有效地发生在这里的是,编译器能够巧妙地推断右侧需要的是什么,因为你声明了左手边。

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>被定义为functional interface

  

这是一个功能界面,因此可以用作   lambda表达式或方法引用的赋值目标。

答案 4 :(得分:2)

界面Comparator功能界面,这意味着此界面只能包含一个抽象方法。

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

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

答案 5 :(得分:1)

首先,您要创建一个新的匿名类(具有行为的方法)。

在第二种方式中,您只是暴露行为(认为这是一种共享函数的方法,一种方法,即使它是透明创建的,也不会看到周围的类)。

我记得在Java Tutorial - The Lambda Expressions

中已经清楚地解释过了
  

匿名类的一个问题是,如果执行你的   匿名类非常简单,例如包含的接口   只有一种方法,然后可能会出现匿名类的语法   笨拙且不清楚。在这些情况下,你通常会试图通过   功能作为另一种方法的参数,例如什么动作   当有人点击按钮时应该采取。 Lambda表达式   使您能够执行此操作,将功能视为方法参数,或   代码作为数据。

我建议您专注于使用lambdas,您只是尝试将行为暴露给某个类或组件,您的示例可能是Collections.sort

lambdas让你有机会有一个更清晰简单的表达式,避免使用匿名类声明的样板。

答案 6 :(得分:1)

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

使用Java8 Comparator@FunctionalInterfaceJavaDoc),可以使用lambda表达式。也就是说,您可以使用返回类型int定义Bi-Function(具有两个参数的函数)并将其用作Comparator,这在第二个示例中完成。两个Comparator实例可以在剩余代码中以完全相同的方式使用。

答案 7 :(得分:1)

Lambda 表达式只是功能界面的简称(只有一个函数的界面),您不需要只编写新的/函数名称在( yourParameterListHere )然后->写入参数列表,然后写下要做/返回的事件(即函数体)。您也可以使用{ }来编写

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

答案 8 :(得分:1)

你可以将lambda视为一种不属于某个类的方法,它可以像一段数据一样传递。

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

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

  

功能界面:

     

这是一个功能界面,因此可以用作任务   lambda表达式或方法引用的目标。

这是一种技术方式,因为它们只有一种方法,而且它们被标记为 functional ,编译器可以将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()));
相关问题