JDK8 已经发布快 4 年的时间了, 现在来谈它的新特性显得略微的有点不合时宜尽管 JDK8 已不再新, 但它的重要特性之一 Lambda 表达式依然是不被大部分开发者所熟练运用, 甚至不被开发者所熟知
国内的开发环境大家都知道, 有各种的老项目, 有各种各样的发布风险, 让公司以及项目组对新的技术往往望而却步, 有公司甚至时至今日还在使用 JDK6 来进行项目开发, 这导致了在很多技术的选择上受到了很大限制, 进而不能跟随时代的脚步使得项目甚至公司一步一步走向衰落
本文简单认识 JDK8 的重要新特性之一 Lambda 表达式 在 JDK8 之前, Java 是不支持函数式编程的, 所谓的函数编程, 即可理解是将一个函数 (也称为行为) 作为一个参数进行传递通常我们提及得更多的是面向对象编程, 面向对象编程是对数据的抽象 (各种各样的 POJO 类), 而函数式编程则是对行为的抽象(将行为作为一个参数进行传递) 在 JavaScript 中这是很常见的一个语法特性, 但在 Java 中将一个函数作为参数传递这却行不通, 好在 JDK8 的出现打破了 Java 的这一限制
认识 Lambda 表达式
首先来引入一个示例, 不知给是否有在 IDEA 编写代码的经历, 如果在 JDK8 的环境下如下所示按照 Java 传统的语法规则编写一个线程
- new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("Hello World!");
- }
- });
IDEA 会给出提示可以使用 Lambda 表达式替换
使用 Lambda 表达式则只需要使用一句话就可代替上面使用匿名类的方式
new Thread(() - >System.out.println("Hello World!"));
在这个例子中, 传统的语法规则, 我们是将一个匿名内部类作为参数进行传递, 我们实现了 Runnable 接口, 并将其作为参数传递给 Thread 类, 这实际上我们传递的是一段代码, 也即我们将代码作为了数据进行传递, 这就带来许多不必要的样板代码
Lambda 表达式一共有三部分组成:
后面的示例中我们会详解这个结构, 包括有无参数, 有无返回值的问题 那么这个看起来奇奇怪怪的不太像 Java 的语法规则, 其本身含义到底什么呢? 这也是开始困扰我的问题, 什么时候在什么场景下可以使用 Lambda 表达式
能够接收 Lambda 表达式的参数类型, 是一个只包含一个方法的接口只包含一个方法的接口称之为函数接口
例如上面创建一个线程的示例, Runnable 接口只包含一个方法, 所以它被称为函数接口, 所以它可以使用 Lambad 表达式来代替匿名内部类根据这个规则, 我们试着来写一个函数接口, 并使用 Lambda 表达式作为参数传递
- package com.coderbuff.custom;
- /**
- * 函数接口: 只有一个方法的接口作为 Lambda 表达式的类型
- * Created by Kevin on 2018/2/17.
- */
- public interface FunctionInterface {
- void test();
- }
测试:
- package com.coderbuff.custom;
- import org.junit.Test;
- /**
- * 函数接口测试
- * Created by Kevin on 2018/2/17.
- */
- public class FunctionInterfaceTest {
- @Test
- public void testLambda() {
- func(new FunctionInterface() {
- @Override
- public void test() {
- System.out.println("Hello World!");
- }
- });
- // 使用 Lambda 表达式代替上面的匿名内部类
- func(() -> System.out.println("Hello World"));
- }
- private void func(FunctionInterface functionInterface) {
- functionInterface.test();
- }
- }
可以看到, 只要是一个接口中只包含一个方法, 则可以使用 Lambda 表达式, 这样的接口称之为函数接口
上面的函数接口比较简单不包含参数, 也不包含返回值
我们再来修改 FunctionInterface 函数接口逐步加大 Lambda 表达式的难度包含参数, 不包含返回值
- package com.coderbuff.custom;
- /**
- * 函数接口: 只有一个方法的接口作为 Lambda 表达式的类型
- * Created by Kevin on 2018/2/17.
- */
- public interface FunctionInterface {
- void test(int param);
- }
测试:
- package com.coderbuff.custom;
- import org.junit.Test;
- /**
- * 函数接口测试
- * Created by Kevin on 2018/2/17.
- */
- public class FunctionInterfaceTest {@Test public void testLambda() {
- // 使用 Lambda 表达式代替匿名内部类
- func((x) - >System.out.println("Hello World" + x));
- }
- private void func(FunctionInterface functionInterface) {
- int x = 1;
- functionInterface.test(x);
- }
- }
关注 Lambda 表达式(x) -> Sysout.out.println("Hello World" + x), 左边传递的是参数, 此处并没有指明参数类型, 因为它可以通过上下文进行类型推导, 但在有些情况下不能推导出参数类型(在编译时不能推导通常 IDE 会提示), 此时则需要指明参数类型我个人建议, 任何情况下指明函数的参数类型
哪种情况不能推导出参数类型呢? 就是函数接口是一个泛型的时候
- package com.coderbuff.custom;
- /**
- * 函数接口: 只有一个方法的接口作为 Lambda 表达式的类型
- * Created by Kevin on 2018/2/17.
- */
- public interface FunctionInterface < T > {
- void test(T param);
- }
测试:
- package com.coderbuff.custom;
- import org.junit.Test;
- /**
- * 函数接口测试
- * Created by Kevin on 2018/2/17.
- */
- public class FunctionInterfaceTest {@Test public void testLambda() {
- // 使用 Lambda 表达式代替匿名内部类
- func((Integer x) - >System.out.println("Hello World" + x));
- }
- private void func(FunctionInterface < Integer > functionInterface) {
- int x = 1;
- functionInterface.test(x);
- }
- }
上面的示例提到了 Lambda 表达式的两种情况:
无参数, 无返回值;
有参数, 无返回值
接下来就是有参数, 有返回值这种较为复杂的情况
- package com.coderbuff.custom;
- /**
- * 函数接口: 只有一个方法的接口作为 Lambda 表达式的类型
- * Created by Kevin on 2018/2/17.
- */
- public interface FunctionInterface < T > {
- boolean test(T param);
- }
测试:
- package com.coderbuff.custom;
- import org.junit.Test;
- /**
- * 函数接口测试
- * Created by Kevin on 2018/2/17.
- */
- public class FunctionInterfaceTest {@Test public void testLambda() {
- // 使用 Lambda 表达式代替匿名内部类
- func((Integer x) - >true);
- }
- private void func(FunctionInterface < Integer > functionInterface) {
- int x = 1;
- functionInterface.test(x);
- }
- }
此时的 Lambda 表达式(Integer x) -> true, 右边是表达式的主体, 直接返回 true, 如果有多行代码, 则可以直接使用花括号表示, 例如:
- func((Integer x) - >{
- System.out.println("Hello World" + x);
- return true;
- });
Lambda 表达式基本的语法规则:
无参数, 无返回值;
有参数, 无返回值;
有参数, 有返回值
这三种基本情况已经大致清楚了, 特别是需要弄清, 什么时候可以使用 Lambda 表达式代替匿名内部类, 也就是 Lambda 表达式的应用场景是函数接口 Lambda 表达式这一新特性在 JDK8 中的引入, 更大的好处则是集合 API 的更新, 新增的 Stream 类库, 使得我们在遍历使用集合时不再像以往那样不断地使用 for 循环
JDK8 使用集合的正确姿势
示例: 计算来自 chengdu 的学生数量有多少
在 JDK8 前的代码:
- for (Student student: studentList) {
- if (student.getCity().equals("chengdu")) {
- count++;
- }
- }
JDK8 使用集合的正确姿势:
count = studentList.stream().filter((student - >student.getCity().equals("chengdu"))).count();
API 的使用难度恰似提高了, 实际只是不熟悉而已传统迭代的方式需要阅读完整个循环才能明白代码逻辑, JDK8 通过流的方式则可以望文生义且代码量大大减小
其中最为重要的是 Stream 流 Stream 的是通过函数式编程方式实现的在集合类上进行复杂操作的工具若要详细讲解 Stream 的实现方式我相信再写一篇博客也不为过, 所以此处不再考查 Stream 的内部实现这里是想告诉大家, 如果有幸使用 JDK8 的开发环境进行开发, 尽量学习使用新的集合操作 API
上面对于 Lambda 表达式以及函数式编程仅仅只是到了一个认识的地步, 似乎只是感受到了缩小代码量, 本文对于 Lambda 式的认识不深入更多的是对于后面更多的知识做一个铺垫或者作为一个扫盲贴, 有关 Lambda 表达式的应用太多, 并发编程响应式编程等等如果你有关于 Lambda 表达式或者函数式编程有更好的见解不妨留
来源: http://www.bubuko.com/infodetail-2499918.html