在 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
作为参考变量吗?
我真的不明白这一点,有人可以解释一下吗?谢谢。
答案 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
是@FunctionalInterface
(JavaDoc),可以使用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()));