Java Lambda表达式初探
前言
本文受启发于Trisha Gee在JavaOne 2016的主题演讲Refactoring to Java 8。
Java 8已经发行两年多,但很多人仍然在使用JDK7。对企业来说,技术上谨慎未必是坏事,但对个人学习而言,不去学习新技术就很可能被技术抛弃。Java 8一个重要的变更是引入Lambda表达式(lambda expression
),这听起来似乎很牛,有种我虽然不知道Lambda表达式是什么,但我仍然觉得很厉害的感觉。不要怕,具体到语言层面上Lambda表达式不过是一种新的语法而已,有了它,Java将开启函数式编程的大门。
为什么需要Lambda表达式
不要纠结什么是Lambda表达式、什么是函数式编程。先来看一下Java 8新的语法特性带来的便利之处,相信你会过目不忘的。
在有Lambda表达式之前,要新建一个线程,需要这样写:
new Thread(new Runnable(){ @Override public void run(){ System.out.println("Thread run()"); } }).start();
有Lambda表达式之后,则可以这样写:
new Thread(() -> System.out.println("Thread run()") ).start();
正如你所见,之前无用的模板代码不见了!如上所示,Lambda表达式一个常见的用法是取代(某些)匿名内部类,但Lambda表达式的作用不限于此。
Lambda表达式的原理
刚接触Lambda表达式可能觉得它很神奇:不需要声明类或者方法的名字,就可以直接定义函数。这看似是编译器为匿名内部类简写提供的一个小把戏,但事实上并非如此,Lambda表达式实际上是通过invokedynamic指令来实现的。先别管这么多,下面是Lambda表达式几种可能的书写形式,“看起来”并不是很难理解。
Runnable run = () -> System.out.println("Hello World");// 1 ActionListener listener = event -> System.out.println("button clicked");// 2 Runnable multiLine = () -> {// 3 System.out.println("Hello "); System.out.println("World"); }; BinaryOperator<Long> add = (Long x, Long y) -> x + y;// 4 BinaryOperator<Long> addImplicit = (x, y) -> x + y;// 5
通过上例可以发现:
- Lambda表达式是有类型的,赋值操作的左边就是类型。Lambda表达式的类型实际上是对应接口的类型。
- Lambda表达式可以包含多行代码,需要用大括号把代码块括起来,就像写函数体那样。
- 大多数时候,Lambda表达式的参数表可以省略类型,就像代码2和5那样。这得益于javac的类型推导机制,编译器可以根据上下文推导出类型信息。
表面上看起来每个Lambda表达式都是原来匿名内部类的简写形式,该内部类实现了某个函数接口(Functional Interface
),但事实比这稍微复杂一些,这里不再展开。所谓函数接口是指内部只有一个接口函数的接口。Java是强类型语言,无论有没有显式指明,每个变量和对象都必须有明确的类型,没有显式指定的时候编译器会尝试确定类型。Lambda表达式的类型就是对应函数接口的类型。
Lambda表达式和Stream
Lambda表达式的另一个重要用法,是和Stream一起使用。Stream is a sequence of elements supporting sequential and parallel aggregate operations。Stream就是一组元素的序列,支持对这些元素进行各种操作,而这些操作是通过Lambda表达式指定的。可以把Stream看作Java Collection的一种视图,就像迭代器是容器的一种视图那样(但Stream不会修改容器中的内容)。下面例子展示了Stream的常见用法。
例子1
假设需要从一个字符串列表中选出以数字开头的字符串并输出,Java 7之前需要这样写:
List<String> list = Arrays.asList("1one", "two", "three", "4four"); for(String str : list){ if(Character.isDigit(str.charAt(0))){ System.out.println(str); } }
而Java 8就可以这样写:
List<String> list = Arrays.asList("1one", "two", "three", "4four"); list.stream()// 1.得到容器的Steam .filter(str -> Character.isDigit(str.charAt(0)))// 2.选出以数字开头的字符串 .forEach(str -> System.out.println(str));// 3.输出字符串
上述代码首先1. 调用List.stream()
方法得到容器的Stream,2. 然后调用filter()
方法过滤出以数字开头的字符串,3. 最后调用forEach()
方法输出结果。
使用Stream有两个明显的好处:
- 减少了模板代码,只用Lambda表达式指明所需操作,代码语义更加明确、便于阅读。
- 将外部迭代改成了Stream的内部迭代,方便了JVM本身对迭代过程做优化(比如可以并行迭代)。
例子2
假设需要从一个字符串列表中,选出所有不以数字开头的字符串,将其转换成大写形式,并把结果放到新的集合当中。Java 8书写的代码如下:
List<String> list = Arrays.asList("1one", "two", "three", "4four"); Set<String> newList = list.stream()// 1.得到容器的Stream .filter(str -> !Character.isDigit(str.charAt(0)))// 2.选出不以数字开头的字符串 .map(String::toUpperCase)// 3.转换成大写形式 .collect(Collectors.toSet());// 4.生成结果集
上述代码首先1. 调用List.stream()
方法得到容器的Stream,2. 然后调用filter()
方法选出不以数字开头的字符串,3. 之后调用map()
方法将字符串转换成大写形式,4. 最后调用collect()
方法将结果转换成Set
。这个例子还向我们展示了方法引用(method references
,代码中标号3处)以及收集器(Collector
,代码中标号4处)的用法,这里不再展开说明。
通过这个例子我们看到了Stream链式操作,即多个操作可以连成一串。不用担心这会导致对容器的多次迭代,因为不是每个Stream的操作都会立即执行。Stream的操作分成两类,一类是中间操作(intermediate operations
),另一类是结束操作(terminal operation
),只有结束操作才会导致真正的代码执行,中间操作只会做一些标记,表示需要对Stream进行某种操作。这意味着可以在Stream上通过关联多种操作,但最终只需要一次迭代。如果你熟悉Spark RDD,对此应该并不陌生。
结语
Java 8引入Lambda表达式,从此打开了函数式编程的大门。如果你之前不了解函数式编程,不必纠结于这个概念。编程过程中简洁明了的书写形式以及强大的Stream API会让你很快熟悉Lambda表达式的。
本文只对Java Lambda表达式的基本介绍,希望能够激发读者对Java函数式编程的兴趣。如果本文能够让你觉得Lambda表达式很好玩,函数式编程很有趣,并产生了进一步学习的欲望,那就再好不过了。文末参考文献中列出了一些有用的资源。
参考文献
http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/Lambda-QuickStart/index.html
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html
http://www.slideshare.net/trishagee/refactoring-to-java-8-devoxx-uk
《Java 8函数式编程 [英]沃伯顿》
https://www.oracle.com/javaone/speakers.html#gee
Java Lambda表达式初探相关推荐
- java游侠_Java Lambda表达式初探
Java Lambda表达式初探 前言 Java 8已经发行两年多,但很多人仍然在使用JDK7.对企业来说,技术上谨慎未必是坏事,但对个人学习而言,不去学习新技术就很可能被技术抛弃.Java 8一个重 ...
- 深入理解Java Lambda表达式,匿名函数,闭包
前言 对于Lambda表达式一直是知其然不知其所以然,为了搞清楚什么是Lambda表达式,以及Lambda表达式的用法和作用,本文应运而生当做学习笔记分享出来,欢迎指正交流. 什么是Lambda 让我 ...
- Java Lambda表达式入门
本文转自:http://blog.csdn.net/renfufei... 转载请注明出处 原文链接: Start Using Java Lambda Expressions 下载示例程序 Examp ...
- java拉姆达表达式事例,Java Lambda表达式详解和实例
简介 Lambda表达式是Java SE 8中一个重要的新特性.lambda表达式允许你通过表达式来代替功能接口. lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体( ...
- Java Lambda表达式forEach无法跳出循环的解决思路
Java Lambda表达式forEach无法跳出循环的解决思路 如果你使用过forEach方法来遍历集合,你会发现在lambda表达式中的return并不会终止循环,这是由于lambda的底层实现导 ...
- Java Lambda 表达式(又名闭包 (Closure)/ 匿名函数 ) 笔记
Java Lambda 表达式(又名闭包 (Closure)/ 匿名函数 ) 笔记 根据 JSR 335, Java 终于在 Java 8 中引入了 Lambda 表达式.也称之为闭包或者匿名函数. ...
- java lambda 表达式中的双冒号和箭头的用法 ::
先构造一些数据,创建一个User类 java lambda 表达式中的双冒号的用法 :: 双冒号运算就是Java中的[方法引用],[方法引用]的格式是 类名::方法名 如下图所示 User是一个类, ...
- Lambda表达式的生动理解以及Java Lambda表达式常见使用场景
本篇承接上一篇 Java Lambda 表达式快速学习 , 对Lambda 表达式的语法格式进行演化说明, 并对Java 中Lambda表达式的常见使用场景进行说明. Lambda 表达式怎么来的? ...
- Java Lambda 表达式快速学习
Lambda 表达式来源于数学, 因为其简洁性,很多开发语言都支持, Java 在版本8 开始引入. Lamba 表达式是什么? 匿名内部类 接口只能有一个需要被实现的方法 Lamba 表达式作用 代 ...
最新文章
- 通过对比对象掩码建议的无监督语义分割
- hdu2008——数值统计
- ACL 2018 论文解读 | 基于深度强化学习的远程监督关系抽取
- postgresql数据库备份与还原
- html怎么设置子目录,如何让主域名指向public_html下的子目录
- linux系统不知道电脑密码怎么办,Linux如何修复系统的Root密码 -电脑资料
- django 1.8 官方文档翻译: 3-5-2 使用Django输出PDF
- oracle 建立一个游戏库,Power Designer怎么新建Oracle数据?建立Oracle数据教程分享
- 安装active directory
- 增长量计算n+1原则_2020黑龙江省考资料分析计算增长量问题.doc
- 微分几何笔记(8) —— 切向量,切空间
- matlab怎么导入数据格式,Matlab导入Excel文件中的数据的详细教程分享
- Tightvncserver 连接树莓派的方法及步骤
- 正则验证车牌号(含新能源)
- j计算机屏幕关闭时间,win7如何设置自动关闭电脑屏幕的时间?
- 我的年假2016-2017
- JeeSite系列之一_JeeSite简介
- 远程桌面连接发生身份验证错误,提示要求的函数不受支持
- Grandpa's Estate POJ - 1228(凸包极角序改写)
- Proteus VSM Studio汇编 + 8位数码管+按键扫描
热门文章
- linux c语言文件拷贝_linux - 远程拷贝文件之scp
- 深入理解 Kotlin coroutine (二)
- 商品规格表设计_400㎡美容院装修设计,为什么说无中式不贵气?
- linux打印机添加命令,Linux Shell脚本系列教程(二):终端打印命令详解
- 数据结构和算法分析:第三章 表、队列和栈
- java query接口_「软帝学院」Java零基础学习详解
- instant.now时区不正确_Centos8如何更改时区
- jaca和mysql外卖系统_【项目实战】太强大了,Java外卖点餐初级系统【附源码】...
- lvs+keepalived实现双实例【双主模型】
- spring boot / cloud (二) 规范响应格式以及统一异常处理