avatar

一条在知识海洋的咸鱼

这个家伙很懒,啥也没有留下😋

  • 首页
  • Linux
  • OCJP
  • Java核心技术卷
  • J2EE相关标准
  • 深入理解Java虚拟机
  • NIO与SOcket编程技术指南
  • Java多线程编程核心技术
  • Redis开发与运维
  • Spring Cloud Alibaba 微服务原理与实践
  • DevOps
  • Docker
  • MySQL必知必会
  • AI自学路线
  • Spring Boot 编程思想(核心篇)
主页 第九章 Lambda表达式
文章

第九章 Lambda表达式

发表于 2021-05-10 更新于 2026-06- 10
作者 Administrator
18~23 分钟 阅读

创建并使用 Lambda 表达式

  • 什么是 lambda 表达式?
    • Lambda 表达式的组成
      • 参数列表
      • 箭头->
      • 代码块
  • 函数式接口和Lambda表达式关系
  • Lambda 表达式与匿名类
    • 相似
    • 区别
    • 为什么局部变量必须为final
  • 总结

什么是 lambda 表达式?

  • 传统
List<Car> compactCars = findCars(cars,
     new Searchable() {
        public boolean test(Car car) {
           return car.getType().equals(
                     CarTypes.COMPACT);
        }
});
  • Java8引入了一种符号,可以:
List compactCars = findCars(cars,
     (Car c) ->
        c.getType().equals(CarTypes.COMPACT)
);

image.png
使用 lambdas,允许你使用一种更简单的方式来编程,这种方式叫做函数式编程,这是一种不同于面向对象程序设计的范式

Lambda 表达式的组成

(Object arg1, int arg2) -> arg1.equals(arg2);

参数列表

  • 一个 lambda 表达式可以有零个,一个或多个参数:
() -> System.out.println("Hi");
(String s) -> System.out.println(s);
(String s1, String s2) -> System.out.println(s1 + s2);
  • 参数的类型可以显式声明,也可以从上下文中推断出来
(s) -> System.out.println(s);
  • 如果只有一个参数,类型就会被推断出来,并且不强制使用括号
s -> System.out.println(s);
// This doesn't compile
String s = ""; s -> System.out.println(s);

箭头->

用于分离参数和主体代码块

代码块

  • Lambda 表达式的主体可以包含一个或多个语句
  • 如果正文有一个语句,则不需要大括号,并返回表达式的值(如果有的话)
() -> 4; (int a) -> a*6;
  • 如果主体包含多个语句,则需要使用大括号,如果表达式返回一个值,则必须返回一个 return 语句
() -> {
     System.out.println("Hi");
     return 4;
}
(int a) -> {
     System.out.println(a);
     return a*6;
}
  • 如果 lambda 表达式没有返回结果,返回语句是可选的,和void方法一样,使用return表示退出
() -> System.out.println("Hi");
() -> {
     System.out.println("Hi");
     return;
}

函数式接口和Lambda表达式关系

  • 函数接口的抽象方法的签名提供了 lambda 表达式的签名(这个签名称为函数描述符)。要使用 lambda 表达式,首先需要一个函数接口
interface Searchable {
     boolean test(Car car);
}
  • 可以创建一个 lambda 表达式,以 Car 对象作为参数并返回一个布尔值
Searchable s = (Car c) -> c.getCostUSD() > 20000;

编译器推断 lambda 表达式可以被分配给 Searchable 接口,仅仅通过它的签名。lambda 表达式并不包含它们正在实现的函数接口的信息。表达式的类型是从使用 lambda 的上下文中推导出来的。当 lambda 表达式被分配给 Searchable 接口时,lambda 必须获取其抽象方法的签名。

  • 使用 lambda 作为方法的参数,编译器将使用方法的定义来推断表达式的类型
class Test {
     public void find() {
         find(c -> c.getCostUSD() > 20000);
     }
     private void find(Searchable s) {
         // Here goes the implementation
     }
}
  • 如果相同的 lambda 表达式具有兼容的抽象方法签名,那么它们可以与不同的函数接口关联。例如:
interface Searchable {
     boolean test(Car car);
}
interface Saleable {
     boolean approve(Car car);
}
//...
Searchable s1 = c -> c.getCostUSD() > 20000;
Saleable s2 = c -> c.getCostUSD() > 20000;
  • Lambda 表达式是一个函数。这和我们知道的方法有点不同,因为它实际上是和一个数学函数相关的。
  • 一个数学函数接受一些输入并产生一些输出,只要我们用相同的参数调用它,它总是返回相同的结果。
  • 可以将 lambda 表达式看作匿名方法(或函数) ,因为它们没有名称,就像匿名类一样

Lambda 表达式与匿名类

表达式是匿名类的一个替代选择,但它们是不一样的

相似

  • 局部变量(方法中定义的变量或参数)只有在声明为最终变量或实际为最终变量时才能使用
  • 可以访问封闭类的实例或静态变量
  • 它们不能抛出比函数接口方法的 throws 子句中指定的更多检查过的异常。只有相同的类型或子类型

区别

  • 对于匿名类,this关键字解析为匿名类本身。对于lambda表达式,它解析为写入lambda的封闭类。
  • 不能从lambda表达式中访问函数接口的默认方法。匿名类可以。
  • 由于不需要加载另一个类,lambda表达式更有效。
  • 匿名类被编译成内部类。但是lambda表达式被转换成其封闭类的私有静态(在某些情况下)方法,并使用invokedynamic指令(在java7中添加)对它们进行动态绑定。

为什么局部变量必须为final

为什么局部变量必须是最终的,而实例或者静态变量在匿名类或者 lambda 表达式中使用时不是这样,这是因为这些变量在 Java 中的实现方式。
实例变量存储在堆(计算机内存中的任何地方都可以访问的区域)上,而局部变量存储在堆栈(计算机内存中存储每个函数创建的临时变量的特殊区域)上。
堆上的变量由多个线程共享,但堆栈上的变量隐式地局限于它们所在的线程。
因此,当您创建匿名内部类或 lambda 表达式的实例时,局部变量的值将被复制。换句话说,您不是使用变量,而是使用复制的值。
首先,这可以防止线程相关的问题。其次,由于您使用的是一个副本,如果变量可以被方法的其余部分修改,那么您将使用一个过时的变量。
final定义的(实际上是最后一个变量)消除了这些可能性。由于该值不能更改,不必担心这些更改是可见的,或者会导致线程问题。

总结

  • Lambda表达式有三个部分:参数列表、箭头和正文:(Object o)->System.out.println(o)
  • 您可以将lambda表达式视为匿名方法(或函数),因为它们没有名称。
  • lambda表达式可以有零(用空括号表示)、一个或多个参数。
  • 参数的类型可以显式声明,也可以从上下文推断。
  • 如果只有一个参数,则类型是推断的,并且不必使用括号。
  • 如果lambda表达式使用与封闭上下文的变量名相同的参数名,则会生成编译错误。
  • 如果主体有一条语句,则不需要大括号,并返回表达式的值(如果有)。
  • 如果主体有多条语句,则需要大括号,如果表达式返回值,则必须返回一条return语句。
  • 如果lambda表达式不返回结果,则返回语句是可选的。
  • 函数接口的抽象方法的签名提供lambda表达式的签名(该签名称为函数描述符)。
  • 要使用lambda表达式,首先需要一个函数接口。但是,lambda表达式不包含有关实现哪个函数接口的信息。
  • 表达式的类型是从使用lambda的上下文中推导出来的。这种类型称为目标类型。
  • 可以推断lambda表达式的目标类型的上下文包括赋值、方法或构造函数参数以及强制转换表达式。
  • 与匿名类一样,lambda表达式可以访问实例和静态变量,但只能访问final或有效的final局部变量。
  • 不能抛出未在函数接口方法的throws子句中定义的异常。
  • lambda表达式,它解析为写入lambda的封闭类。
  • 不能从lambda表达式中访问函数接口的默认方法。
OCJP
许可协议: 
分享

相关文章

9月 6, 2021

关于这篇OCP-Java专题说明

5月 28, 2021

三十章 本地化

通过使用 Locale 对象读取和设置 Locale。创建并读取属性文件。为每个区域设置构建资源包,并在应用程序中加载资源包。本地化设置语言环境本地化设计一个能够处理不同语言和地区的应用程序。最常见的是消息、日期和数字。java.util.LocaleLocale类基本上代表一种语言和一个国家获得机

5月 27, 2021

第二十九章 JDBC API

了解JDBC API 核心的接口,包括 Driver、 Connection、 Statement 和 ResultSet 接口及其与提供者实现的关系。使用 DriverManager 类(包括 JDBC URL)标识连接到数据库所需的组件。从数据库提交查询和读取结果,包括创建语句、返回结果集、遍历

下一篇

第十章 Java内置Lambda接口

上一篇

第八章 函数式接口

最近更新

  • 55 异常兜底:工具失败、非法输出、空检索与超时
  • 54 Agent Memory:短期记忆、用户上下文与摘要
  • 53 Human-in-the-loop:让关键动作暂停、确认与恢复
  • 52 工单 Tool:字段校验、创建结果与失败补偿
  • 51 订单 Tool:Mock、鉴权、状态解析与脱敏

热门标签

java基础 微服务 maven Spring Tomcat DDD Linux Linux基础 SQL基础 数据结构算法

目录

©2026 一条在知识海洋的咸鱼. 保留部分权利。

使用 Halo 主题 Chirpy