- Lambda 表达式
- Lambda 表达式的语法
- Lambda 表达式的理想用例
- 方法 1: 创建搜索匹配一个特征的成员的方法
- 方法 2: 创建更通用的搜索方法
- 方法 3: 在局部类中指定搜索条件代码
- 方法 4: 在匿名类中指定搜索条件代码
- 方法 5: 使用 Lambda 表达式指定搜索条件代码
- 方法 6: 使用带有 Lambda 表达式的标准函数式接口
- 方法 7: 在整个应用程序中使用 Lambda 表达式
- 方法 8: 更广泛地使用泛型
- 方法 9: 使用接受 Lambda 表达式作为参数的聚合操作
- 使用注意点
- 访问封闭作用域的局部变量
- 目标输入
- 目标类型和方法参数
匿名类的一个问题是,如果匿名类的实现非常简单,例如只包含一个方法的接口,那么匿名类的语法可能看起来笨拙且不清楚。 在这些情况下,您通常会尝试将功能作为参数传递给另一个方法,例如当有人单击按钮时应采取的操作。 Lambda 表达式使您能够做到这一点,将功能视为方法参数,或将代码视为数据。 匿名类 向您展示了如何在不给它命名的情况下实现基类。 虽然这通常比命名类更简洁,但对于只有一个方法的类,即使是匿名类也显得有些多余和繁琐。 Lambda 表达式让您可以更紧凑地表达单方法类的实例。
Lambda 表达式的语法一个 lambda 表达式由以下内容组成:
- 括号中的形式参数的逗号分隔列表。 您可以省略 lambda 表达式中参数的数据类型。 此外,如果只有一个参数,您可以省略括号。
(p1,p2)
- 箭头标记,
->
- 主体,由单个表达式或语句块组成。 在 lambda 表达式中,除了 void 方法,您必须将语句括在大括号 (
{}
) 中。 但是,为了代码规范都需要保留大括号。return 语句不是表达式。可以通过return关键字显示的返回值。
请注意,lambda 表达式看起来很像方法声明。 您可以将 lambda 表达式视为匿名方法——没有名称的方法。 以下示例 Calculator
是采用多个形式参数的 lambda 表达式示例:
public class Calculator {
interface IntegerMath {
int operation(int a, int b);
}
public int operateBinary(int a, int b, IntegerMath op) {
return op.operation(a, b);
}
public static void main(String... args) {
Calculator myApp = new Calculator();
IntegerMath addition = (a, b) -> {a + b};
IntegerMath subtraction = (a, b) -> {a - b};
System.out.println("40 + 2 = " +
myApp.operateBinary(40, 2, addition));
System.out.println("20 - 10 = " +
myApp.operateBinary(20, 10, subtraction));
}
}
operateBinary
方法对两个整数操作数执行数学运算。 操作本身由IntegerMath
的一个实例指定。 该示例使用 lambda 表达式定义了两个操作,“加法”和“减法”。 该示例打印以下内容:
40 + 2 = 42
20 - 10 = 10
Lambda 表达式的理想用例
主要利用下方的示例,从“老式的常规实现”,“老式的通用实现”,“局部类的实现方式”,“匿名类的实现”,“部分lambda表达式”,“使用函数式接口的lambda表达式”,“接受 Lambda 表达式作为参数的聚合操作”一步步介绍lambda表达式的使用。
假设您正在创建一个社交网络应用程序。 您希望创建一个功能,使管理员能够对满足特定条件的社交网络应用程序的成员执行任何类型的操作,例如发送消息。 下表详细描述了此用例:
字段名解释NamePerform action on selected members 对选定成员执行操作Primary ActorAdministratorPreconditionsAdministrator is logged in to the system. 管理员已登录系统。PostconditionsAction is performed only on members that fit the specified criteria. 仅对符合指定条件的成员执行操作。Main Success ScenarioAdministrator specifies criteria of members on which to perform a certain action.Administrator specifies an action to perform on those selected members.Administrator selects the Submit button.The system finds all members that match the specified criteria.The system performs the specified action on all matching members.管理员指定执行特定操作的成员标准。管理员指定对这些选定成员执行的操作。管理员选择提交按钮。系统查找符合指定条件的所有成员。系统执行指定的 对所有匹配的成员执行操作。Extensions1a. Administrator has an option to preview those members who match the specified criteria before he or she specifies the action to be performed or before selecting the Submit button. 1a。 管理员可以选择在指定要执行的操作之前或在选择提交按钮之前预览符合指定条件的成员。Frequency of OccurrenceMany times during the day.一天中很多次。假设此社交网络应用程序的成员由以下 Person
类表示:
public class Person {
public enum Sex {
MALE, FEMALE
}
String name;
LocalDate birthday;
Sex gender;
String emailAddress;
public int getAge() {
// ...
}
public void printPerson() {
// ...
}
}
假设您的社交网络应用程序的成员存储在List
实例中。
本节从对此用例的简单方法开始。 它使用局部类和匿名类改进了这种方法,然后使用 lambda 表达式以一种高效而简洁的方法结束。 在示例 RosterTest
中找到本节中描述的代码。
一种简单的方法是创建几种方法; 每种方法都会搜索与某个特征(例如性别或年龄)相匹配的成员。 以下方法打印超过指定年龄的成员:
public static void printPersonsOlderThan(List roster, int age) {
for (Person p : roster) {
if (p.getAge() >= age) {
p.printPerson();
}
}
}
注意:List
是有序的Collection
。 collection 是将多个元素组合成一个单元的对象。集合用于存储、检索、操作和通信聚合数据。有关集合的更多信息,请参阅 Collections 跟踪。
这种方法可能会使您的应用程序变得脆弱,这是由于引入更新(例如更新的数据类型)而导致应用程序无法运行的可能性。假设您升级应用程序并更改Person
类的结构,使其包含不同的成员变量;也许该类使用不同的数据类型或算法记录和测量年龄。您将不得不重写很多 API 以适应这种变化。此外,这种方法具有不必要的限制性;例如,如果您想打印小于某个年龄的成员怎么办?
下面的方法比printPersonsOlderThan
更通用;它打印指定年龄范围内的成员:
public static void printPersonsWithinAgeRange(
List roster, int low, int high) {
for (Person p : roster) {
if (low = 18
&& p.getAge() p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() p.printPerson()
);
如果您想对会员的个人资料做更多的事情而不是打印出来怎么办。 假设您要验证成员的个人资料或检索他们的联系信息? 在这种情况下,您需要一个包含返回值的抽象方法的功能接口。 Function
接口包含方法 R apply(T t)
。 以下方法检索参数mapper
指定的数据,然后对其执行参数block
指定的操作:
public static void processPersonsWithFunction(
List roster,
Predicate tester,
Function mapper,
Consumer block) {
for (Person p : roster) {
if (tester.test(p)) {
String data = mapper.apply(p);
block.accept(data);
}
}
}
以下方法从 roster
中包含有资格参加选择性服务的每个成员中检索电子邮件地址,然后将其打印出来:
processPersonsWithFunction(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() p.getEmailAddress(),
email -> System.out.println(email)
);
方法 8: 更广泛地使用泛型
重新考虑方法processPersonsWithFunction
。 以下是它的通用版本,它接受包含任何数据类型元素的集合作为参数:
public static void processElements(
Iterable source,
Predicate tester,
Function mapper,
Consumer block) {
for (X p : source) {
if (tester.test(p)) {
Y data = mapper.apply(p);
block.accept(data);
}
}
}
要打印有资格参加选择性服务的成员的电子邮件地址,请调用 processElements
方法,如下所示:
processElements(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() p.getEmailAddress(),
email -> System.out.println(email)
);
此方法调用执行以下操作:
- 从集合
source
中获取对象的来源。在此示例中,它从集合roster
中获取Person
对象的来源。请注意,集合roster
是一个 List 类型的集合,也是一个Iterable
类型的对象。 - 过滤匹配
Predicate
对象tester
的对象。在此示例中,Predicate
对象是一个 lambda 表达式,用于指定哪些成员有资格获得选择性服务。 - 将每个过滤的对象映射到由
Function
对象mapper
指定的值。在这个例子中,Function
对象是一个返回成员电子邮件地址的 lambda 表达式。 - 对由
Consumer
对象block
指定的每个映射对象执行一个动作。在本例中,Consumer
对象是一个打印字符串的 lambda 表达式,该字符串是Function
对象返回的电子邮件地址。
您可以将这些操作中的每一个替换为聚合操作。
方法 9: 使用接受 Lambda 表达式作为参数的聚合操作以下示例使用聚合操作来打印集合 roster
中符合选择性服务条件的成员的电子邮件地址:
roster
.stream()
.filter(
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() p.getEmailAddress())
.forEach(email -> System.out.println(email));
下表映射了方法 processElements
执行的每个操作与相应的聚合操作:
processElements
ActionAggregate Operation获取对象的来源Stream **stream**()
过滤与Predicate
对象匹配的对象Stream **filter**(Predicate
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?