0

0

Java Lambda 表达式详解及示例代码

高洛峰

高洛峰

发布时间:2017-01-23 13:24:52

|

1714人浏览过

|

来源于php中文网

原创

java lambda 表达式是 java 8 引入的一个新的功能,可以说是模拟函数式编程的一个语法糖,类似于 javascript 中的闭包,但又有些不同,主要目的是提供一个函数化的语法来简化我们的编码。

Lambda 基本语法

Lambda 的基本结构为 (arguments) -> body,有如下几种情况:

参数类型可推导时,不需要指定类型,如 (a) -> System.out.println(a)

当只有一个参数且类型可推导时,不强制写 (), 如 a -> System.out.println(a)

立即学习Java免费学习笔记(深入)”;

参数指定类型时,必须有括号,如 (int a) -> System.out.println(a)

参数可以为空,如 () -> System.out.println(“hello”)

body 需要用 {} 包含语句,当只有一条语句时 {} 可省略

常见的写法如下:

(a) -> a * a
(int a, int b) -> a + b
(a, b) -> {return a - b;}
() -> System.out.println(Thread.currentThread().getId())

函数式接口 FunctionalInterface

概念

Java Lambda 表达式以函数式接口为基础。什么是函数式接口(FunctionalInterface)? 简单说来就是只有一个方法(函数)的接口,这类接口的目的是为了一个单一的操作,也就相当于一个单一的函数了。常见的接口如:Runnable, Comparator 都是函数式接口,并且都标注了注解 @FunctionalInterface 。

举例

以 Thread 为例说明很容易理解。Runnable 接口是我们线程编程时常用的一个接口,就包含一个方法 void run(),这个方法就是线程的运行逻辑。按照以前的语法,我们新建线程一般要用到 Runnable 的匿名类,如下:

new Thread(new Runnable() {
  @Override
  public void run() {
    System.out.println(Thread.currentThread().getId());
  }
 
}).start();

如果写多了,是不是很无聊,而基于 Lambda 的写法则变得简洁明了,如下:

new Thread(() -> System.out.println(Thread.currentThread().getId())).start();

注意 Thread 的参数,Runnable 的匿名实现就通过一句就实现了出来,写成下面的更好理解

Runnable r = () -> System.out.println(Thread.currentThread().getId());
new Thread(r).start();

当然 Lambda 的目的不仅仅是写起来简洁,更高层次的目的等体会到了再总结。

再看一个比较器的例子,按照传统的写法,如下:

Integer[] a = {1, 8, 3, 9, 2, 0, 5};
Arrays.sort(a, new Comparator<Integer>() {
  @Override
  public int compare(Integer o1, Integer o2) {
    return o1 - o2;
  }
});

Lambda 表达式写法如下:

Integer[] a = {1, 8, 3, 9, 2, 0, 5};
Arrays.sort(a, (o1, o2) -> o1 - o2);

JDK中的函数式接口

为了现有的类库能够直接使用 Lambda 表达式,Java 8 以前存在一些接口已经被标注为函数式接口的:

java.lang.Runnable

java.util.Comparator

java.util.concurrent.Callable

java.io.FileFilter

java.security.PrivilegedAction

java.beans.PropertyChangeListener

Java 8 中更是新增加了一个包 java.util.function,带来了常用的函数式接口:

Function - 函数:输入 T 输出 R

BiFunction - 函数:输入 T 和 U 输出 R 对象

Predicate - 断言/判断:输入 T 输出 boolean

BiPredicate - 断言/判断:输入 T 和 U 输出 boolean

Supplier - 生产者:无输入,输出 T

Consumer - 消费者:输入 T,无输出

BiConsumer - 消费者:输入 T 和 U 无输出

UnaryOperator - 单元运算:输入 T 输出 T

BinaryOperator - 二元运算:输入 T 和 T 输出 T

另外还对基本类型的处理增加了更加具体的函数是接口,包括:BooleanSupplier, DoubleBinaryOperator, DoubleConsumer, DoubleFunction, DoublePredicate, DoubleSupplier, DoubleToIntFunction, DoubleToLongFunction, DoubleUnaryOperator, IntBinaryOperator, IntConsumer, IntFunction, IntPredicate, IntSupplier, IntToDoubleFunction, IntToLongFunction, IntUnaryOperator, LongBinaryOperator, LongConsumer,LongFunction, LongPredicate, LongSupplier, LongToDoubleFunction,LongToIntFunction, LongUnaryOperator, ToDoubleBiFunction, ToDoubleFunction,ToIntBiFunction, ToIntFunction, ToLongBiFunction, ToLongFunction 。结合上面的函数式接口,对这些基本类型的函数式接口通过类名就能一眼看出接口的作用。

创建函数式接口

有时候我们需要自己实现一个函数式接口,做法也很简单,首先你要保证此接口只能有一个函数操作,然后在接口类型上标注注解 @FunctionalInterface 即可。

类型推导

类型推导是 Lambda 表达式的基础,类型推导的过程就是 Lambda 表达式的编译过程。以下面的代码为例:

Function strToInt = str -> Integer.parseInt(str);
编译期间,我理解的类型推导的过程如下:

先确定目标类型 Function

Function 作为函数式接口,其方法签名为:Integer apply(String t)

检测 str -> Integer.parseInt(str) 是否与方法签名匹配(方法的参数类型、个数、顺序 和返回值类型)

如果不匹配,则报编译错误

这里的目标类型是关键,通过目标类型获取方法签名,然后和 Lambda 表达式做出对比。

方法引用

方法引用(Method Reference)的基础同样是函数式接口,可以直接作为函数式接口的实现,与 Lambda 表达式有相同的作用,同样依赖于类型推导。方法引用可以看作是只调用一个方法的 Lambda 表达式的简化。

方法引用的语法为: Type::methodName 或者 instanceName::methodName , 构造函数对应的 methodName 为 new。

例如上面曾用到例子:

Function strToInt = str -> Integer.parseInt(str);

对应的方法引用的写法为

Function strToInt = Integer::parseInt;

根据方法的类型,方法引用主要分为一下几种类型,构造方法引用、静态方法引用、实例上实例方法引用、类型上实例方法引用等

构造方法引用

语法为: Type::new 。 如下面的函数为了将字符串转为数组

方法引用写法

Function strToInt = Integer::new;

Lambda 写法

PHP 网络编程技术与实例(曹衍龙)
PHP 网络编程技术与实例(曹衍龙)

PHP网络编程技术详解由浅入深,全面、系统地介绍了PHP开发技术,并提供了大量实例,供读者实战演练。另外,笔者专门为本书录制了相应的配套教学视频,以帮助读者更好地学习本书内容。这些视频和书中的实例源代码一起收录于配书光盘中。本书共分4篇。第1篇是PHP准备篇,介绍了PHP的优势、开发环境及安装;第2篇是PHP基础篇,介绍了PHP中的常量与变量、运算符与表达式、流程控制以及函数;第3篇是进阶篇,介绍

下载

Function strToInt = str -> new Integer(str);

传统写法

Function<String, Integer> strToInt = new Function<String, Integer>() {
  @Override
  public Integer apply(String str) {
    return new Integer(str);
  }
};

数组构造方法引用

语法为: Type[]::new 。如下面的函数为了构造一个指定长度的字符串数组

方法引用写法

Function fixedArray = String[]::new;

方法引用写法

Function fixedArray = length -> new String[length];

传统写法

Function<Integer, String[]> fixedArray = new Function<Integer, String[]>() {
  @Override
  public String[] apply(Integer length) {
    return new String[length];
  }
};

静态方法引用

语法为: Type::new 。 如下面的函数同样为了将字符串转为数组

方法引用写法

Function strToInt = Integer::parseInt;

Lambda 写法

Function strToInt = str -> Integer.parseInt(str);

传统写法

Function<String, Integer> strToInt = new Function<String, Integer>() {
  @Override
  public Integer apply(String str) {
    return Integer.parseInt(str);
  }
};

实例上实例方法引用

语法为: instanceName::methodName 。如下面的判断函数用来判断给定的姓名是否在列表中存在

List names = Arrays.asList(new String[]{"张三", "李四", "王五"});
Predicate checkNameExists = names::contains;
System.out.println(checkNameExists.test("张三"));
System.out.println(checkNameExists.test("张四"));

类型上实例方法引用

语法为: Type::methodName 。运行时引用是指上下文中的对象,如下面的函数来返回字符串的长度

Function<String, Integer> calcStrLength = String::length;
System.out.println(calcStrLength.apply("张三"));
List<String> names = Arrays.asList(new String[]{"zhangsan", "lisi", "wangwu"});
names.stream().map(String::length).forEach(System.out::println);<br>

   

又比如下面的函数已指定的分隔符分割字符串为数组

BiFunction split = String::split;
String[] names = split.apply("zhangsan,lisi,wangwu", ",");
System.out.println(Arrays.toString(names));

Stream 对象

概念

什么是 Stream ? 这里的 Stream 不同于 io 中的 InputStream 和 OutputStream,Stream 位于包 java.util.stream 中, 也是 java 8 新加入的,Stream 只的是一组支持串行并行聚合操作的元素,可以理解为集合或者迭代器的增强版。什么是聚合操作?简单举例来说常见的有平均值、最大值、最小值、总和、排序、过滤等。

Stream 的几个特征:

单次处理。一次处理结束后,当前Stream就关闭了。
支持并行操作
常见的获取 Stream 的方式

从集合中获取

Collection.stream();
Collection.parallelStream();

静态工厂

Arrays.stream(array)
Stream.of(T …)
IntStream.range()
这里只对 Stream 做简单的介绍,下面会有具体的应用。要说 Stream 与 Lambda 表达式有什么关系,其实并没有什么特别紧密的关系,只是 Lambda 表达式极大的方便了 Stream 的使用。如果没有 Lambda 表达式,使用 Stream 的过程中会产生大量的匿名类,非常别扭。

举例

以下的demo依赖于 Employee 对象,以及由 Employee 对象组成的 List 对象。

public class Employee {
 
  private String name;
  private String sex;
  private int age;
 
  public Employee(String name, String sex, int age) {
    super();
    this.name = name;
    this.sex = sex;
    this.age = age;
  }
  public String getName() {
    return name;
  }
 
  public String getSex() {
    return sex;
  }
  public int getAge() {
    return age;
  }
  @Override
  public String toString() {
    StringBuilder builder = new StringBuilder();
    builder.append("Employee {name=").append(name).append(", sex=").append(sex).append(", age=").append(age)
        .append("}");
    return builder.toString();
  }
}
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("张三", "男", 25));
employees.add(new Employee("李四", "女", 24));
employees.add(new Employee("王五", "女", 23));
employees.add(new Employee("周六", "男", 22));
employees.add(new Employee("孙七", "女", 21));
employees.add(new Employee("刘八", "男", 20));

   

打印所有员工

Collection 提供了 forEach 方法,供我们逐个操作单个对象。

employees.forEach(e -> System.out.println(e)); 
或者
employees.stream().forEach(e -> System.out.println(e));

按年龄排序

Collections.sort(employees, (e1, e2) -> e1.getAge() - e2.getAge());
employees.forEach(e -> System.out.println(e));
或者
employees.stream().sorted((e1, e2) -> e1.getAge() - e2.getAge()).forEach(e -> System.out.println(e));
打印年龄最大的女员工

max/min 返回指定排序条件下最大/最小的元素

Employee maxAgeFemaleEmployee = employees.stream()
    .filter(e -> "女".equals(e.getSex()))
    .max((e1, e2) -> e1.getAge() - e2.getAge())
    .get();
System.out.println(maxAgeFemaleEmployee);

   

打印出年龄大于20 的男员工

filter 可以过滤出符合条件的元素

employees.stream()
        .filter(e -> e.getAge() > 20 && "男".equals(e.getSex()))
        .forEach(e -> System.out.println(e));
打印出年龄最大的2名男员工

limit 方法截取有限的元素

employees.stream()
    .filter(e -> "男".equals(e.getSex()))
    .sorted((e1, e2) -> e2.getAge() - e1.getAge())
    .limit(2)
    .forEach(e -> System.out.println(e));

   

打印出所有男员工的姓名,使用 , 分隔

map 将 Stream 中所有元素的执行给定的函数后返回值组成新的 Stream

String maleEmployeesNames = employees.stream()
    .map(e -> e.getName())
    .collect(Collectors.joining(","));
System.out.println(maleEmployeesNames);

   

统计信息

IntSummaryStatistics, DoubleSummaryStatistics, LongSummaryStatistics 包含了 Stream 中的汇总数据。

IntSummaryStatistics stat = employees.stream()
    .mapToInt(Employee::getAge).summaryStatistics();
System.out.println("员工总数:" + stat.getCount());
System.out.println("最高年龄:" + stat.getMax());
System.out.println("最小年龄:" + stat.getMin());
System.out.println("平均年龄:" + stat.getAverage());

   

总结

Lambda 表达式确实可以减少很多代码,能提高生产力,当然也有弊端,就是复杂的表达式可读性会比较差,也可能是还不是很习惯的缘故吧,如果习惯了,相信会喜欢上的。凡事都有两面性,就看我们如何去平衡这其中的利弊了,尤其是在一个团队中。

以上就是对Java8 JavaLambda 的资料整理,后续继续补充相关资料谢谢大家对本站的支持!

更多Java Lambda 表达式详解及示例代码相关文章请关注PHP中文网!

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

928

2026.02.13

微博网页版主页入口与登录指南_官方网页端快速访问方法
微博网页版主页入口与登录指南_官方网页端快速访问方法

本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。

307

2026.02.13

Flutter跨平台开发与状态管理实战
Flutter跨平台开发与状态管理实战

本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。

183

2026.02.13

TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

29

2026.02.13

Redis高可用架构与分布式缓存实战
Redis高可用架构与分布式缓存实战

本专题围绕 Redis 在高并发系统中的应用展开,系统讲解主从复制、哨兵机制、Cluster 集群模式及数据分片原理。内容涵盖缓存穿透与雪崩解决方案、分布式锁实现、热点数据优化及持久化策略。通过真实业务场景演示,帮助开发者构建高可用、可扩展的分布式缓存系统。

103

2026.02.13

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

54

2026.02.12

雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法
雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法

本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。

17

2026.02.12

豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法
豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法

本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。

764

2026.02.12

PostgreSQL性能优化与索引调优实战
PostgreSQL性能优化与索引调优实战

本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。

92

2026.02.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
AngularJS教程
AngularJS教程

共24课时 | 3.8万人学习

【李炎恢】ThinkPHP8.x 后端框架课程
【李炎恢】ThinkPHP8.x 后端框架课程

共50课时 | 4.6万人学习

php初学者入门课程
php初学者入门课程

共10课时 | 0.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号