Java Lambda表达式初探

前言

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 add = (Long x, Long y) -> x + y;// 4

BinaryOperator 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 list = Arrays.asList("1one", "two", "three", "4four");

for(String str : list){

if(Character.isDigit(str.charAt(0))){

System.out.println(str);

}

}

而Java 8就可以这样写:

List 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 list = Arrays.asList("1one", "two", "three", "4four");

Set 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表达式很好玩,函数式编程很有趣,并产生了进一步学习的欲望,那就再好不过了。文末参考文献中列出了一些有用的资源。

致谢

非常感谢阿里巴巴“西行游学计划”给予我们的支持,感谢阿里中间件团队资助我们远美国赴旧金山参加Java语言的顶级技术会议——2016 JaveOne大会。借此我们有机会跟国际顶尖大牛面对面的交流,并第一时间了解到Java语言的最新动态。同样感谢伴我一起游学的两位队友,有ta们在异国他乡的陪伴和关照,我的西行之旅才更加丰富多彩。这次游学给我们带来的惊喜,将深深留存在我们的记忆当中。

希望更多的同学能够关注并参与到阿里巴巴天池大赛当中,也希望阿里能够继续坚持对青年学生的支持和培养!

参考文献

java游侠_Java Lambda表达式初探相关推荐

  1. Java Lambda表达式初探

    前言 本文受启发于Trisha Gee在JavaOne 2016的主题演讲Refactoring to Java 8. Java 8已经发行两年多,但很多人仍然在使用JDK7.对企业来说,技术上谨慎未 ...

  2. java junit 异常_JUnit:使用Java 8和Lambda表达式测试异常

    java junit 异常 在JUnit中,有许多方法可以在测试代码中测试异常,包括try-catch idiom JUnit @Rule和catch-exception库. 从Java 8开始,我们 ...

  3. 带有Java 8,lambda表达式和Mockito-Java8附加组件的更紧凑的Mockito

    Mockito-Java8是一组Mockito附加组件,它们利用Java 8和lambda表达式使Mockito的模拟更加紧凑. 在2015年初,我进行了简短的演讲, Java 8为测试带来了力量! ...

  4. JUnit:使用Java 8和Lambda表达式测试异常

    在JUnit中,有许多方法可以在测试代码中测试异常,包括try-catch idiom JUnit @Rule和catch-exception库. 从Java 8开始,我们还有另一种处理异常的方法:使 ...

  5. Java 8:Lambda表达式与自动关闭

    如果您通过Neo4j的Java API和Java 6使用了Neo4j的早期版本,则可能具有与以下类似的代码,以确保在事务中进行写操作: public class StylesOfTx {public ...

  6. java基础之lambda表达式

    java基础之lambda表达式 1 什么是lambda表达式 lambda表达式是一个匿名函数,允许将一个函数作为另外一个函数的参数,将函数作为参数传递(可理解为一段传递的代码). 2 为什么要用l ...

  7. 如何开始使用Java中的Lambda表达式

    by Luis Santiago 路易斯·圣地亚哥(Luis Santiago) 如何开始使用Java中的Lambda表达式 (How to start working with Lambda Exp ...

  8. Java 8 新增lambda表达式(-)

    2019独角兽企业重金招聘Python工程师标准>>> Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性 ...

  9. 如何在Java中使用Lambda表达式

    如何在Java中使用Lambda表达式 Lambda表达式是Java 8新增的一个非常强大的特性.它使得函数式编程在Java中变得更加容易和直观. Lambda表达式的基础知识 在Java中,Lamb ...

最新文章

  1. 查看OpenStack版本
  2. 牛客网 在线编程 之字形矩阵打印
  3. Google图片加载库Glide的简单封装GlideUtils
  4. Java父类强制转换子类原则
  5. 计算机网络安全管理协议,河西学院校园网络安全管理协议
  6. protobuf java文档_Java中使用Protobuf
  7. IDEA 自动删除无引用依赖
  8. 5G的基站覆盖范围300米,今后边远地区的手机通话怎样保证?
  9. Android修行手册-TextView常用属性篇
  10. 如何在PDF中修改内容?这个方法免费编辑
  11. Java跨年祝福语代码_[商业跨年祝福语]跨年祝福语贺词大全
  12. 图书销售系统 php,php文学小说销售系统
  13. Excel快捷键大全 Excel2013/2010/2007/2003常用快捷键大全【转】
  14. 人脸识别、深度学习优秀设计(毕业专业指导)
  15. stormzhang Android 学习之路
  16. [PTA]实验11-2-7 统计专业人数
  17. 在美国学CS能挣多少钱?美国IT公司标准 offer package详细数字及绿卡政策 | 美国留学申请与就业找工作咨询博客|Warald|一亩三分地论坛...
  18. python T检验
  19. oracle 大表统计分析,oracle表的分析统计
  20. RabbitMQ几种工作模式,实现延时消息的两种案例

热门文章

  1. 3.500句中文句子
  2. 解决CC2530在zstack中无法进入P0中断的问题
  3. 面向对象七大设计原则之依赖倒置原则
  4. java算法集训结果填空题练习1
  5. [VPN]华为SecoClient客户端Linux使用
  6. 01-fink基础知识
  7. 【进度2】从阿里云迁至腾讯云,并添加网站备案号
  8. 绵阳师范学院 计算机科学教育系 李金洹,四川省教育厅专项检查组来我校检查学位论文管理工作...
  9. 数据库中的多值依赖的理解
  10. 从零开始学平面设计需要注意什么