一、为什么需要了解函数式编程
- Java诞生以来变化最大的一个版本
- 计算机体系结构变化:多核(计算集群)
- 业务场景变化:处理大型数据集(大数据)
- 面向软件工程师更友好
- Spring5开始大量使用Java8新特性
Java8提供的新特性
- 函数式编程:Lambda,流,默认方法(这篇重点介绍的)
- 新的时间和日期API
- 新的NPE解决方案Optional
- CompletableFuture异步计算
- 。。。
二、函数式编程
- 就是使用函数进行编程
- 使用无副作用的函数
- 不修改内嵌类的状态
- 不修改其他对象的状态
- 返回所有计算结果
行为参数化——函数式编程成为可能
Lambda——匿名函数
- 匿名函数:没有名称,有参数列表、函数主体、返回类型
- Lambda表达式构成:表达式写法和语句写法
- 合法的Lambda表达式
- 不
- 不合法的Lambda表达式(IntelliJ编译器给出提示)
函数式接口
默认方法——避免逻辑灾难
- default
- 没有显式声明方法的具体实现,就自动继承默认实现
- 平滑进行接口优化与演进
- 抽象公共行为,保持代码的可复用性与功能聚焦
- 一个类只能继承一个抽象类,但可以实现多个接口
- 抽象类可以实例化保存通用状态,接口则不能有实例变量
函数描述符
- 函数式接口的抽象方法
- Predicate的函数描述符:T -> boolean
- Lambda表达式允许用内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例
- Lambda表达式的签名要和相应函数式接口的抽象方法签名一致
原始类型特化——减少拆装箱的性能损耗
- 泛型只能绑定到引用类型
- 自动装箱会把原始类型包裹重新在堆上分配内存
Java8提供的函数式接口及应用举例
自定义函数式接口
方法引用——Lambda的语法糖
三、函数式数据处理
函数式数据处理
- 从如何做到要做什么
流——像使用SQL一样处理数据
- 从支持数据处理操作的源生成的元素序列
- 可声明——说明想要完成什么而不是如何实现
- 可复合——流水线工作
- 可并行——充分利用多核性能
流与集合的比较
- 集合是内存中的数据结构,包含所有值——DVD
- 流是按需计算,延迟创建的集合——在线视频 (但只能遍历一次)
流操作与使用
- 中间操作,除了流水线执行中断操作否则不会执行任何处理,屏蔽掉了外部迭代过程
- 终端操作,结果是任何不是流的值
常用流API
谨慎使用并行流——天下没有免费的午餐
- parallel方法及ForkJoinTask框架
- limit和findFirst等并行流不一定比顺序流快
- 少量元素的并行额外开销
- 数据结构是否容易拆分进行流计算
重构与调试
命令式代码到函数式代码
- 1.行为描述
- 2.使用匿名内部类
- 3.行为参数化,使用Lambda表达式
- 4.使用静态方法简化Lambda
- 5.使用方法引用进一步简化
- 6.复合Lambda
调试Lambda
四、注意事项
默认方法冲突——显式解决
- 类实现了多个接口,并且存在相同签名的默认方法
- FatherClassName.super.defaultMethod(),最好显式解决
明确Lambda表达式的操作含义
- 下面三条语句分别会打印什么?
避免共享可变状态——对输出结果负责
- 取n = 10000,会得到想要的结果嘛?
- 每一次的计算结果都不一样!