江头未是风波恶,别有人间行路难!
——辛弃疾《鹧鸪天》
JDK8新特性—Lambda表达式
1. 函数式编程思想概述
在数学中,
函数就是有输入量、输出量的一套计算方案
,也就是“拿什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”
,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做。
1.1 冗余的Runnable代码
1.2 TreeSet的定制排序
1.3 为什么使用Lambda表达式呢?
Lambda 是一个 匿名函数,我们可以把 Lambda 表达式理解为是 一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
2. Lambda表达式语法☆
Lambda 表达式:
在Java 8 语言中引入的一种新的语法元素和操作符
。这个操作符为 “->”
, 该操作符被称为Lambda 操作符 或 箭头操作符
。它将 Lambda 分为两个部分:
左侧:指定了 Lambda 表达式需要的
参数列表
。无参数则留空,多个参数则用逗号分隔。
-> 是新引入的语法格式,代表指向动作。
右侧:指定了 Lambda 体,是抽象方法的实现逻辑,也即Lambda 表达式要执行的功能。
// Lambda表达式的标准格式为: (参数类型 参数名称) ‐> { 代码语句 } <!--0-->
3.3 自定义函数式接口
MyFunction
1 | /` |
MyFunctionTest
1 | /` |
3.4 Java内置四大核心函数式接口
Supplier接口
java.util.function.Supplier<T>
接口仅包含一个无参的方法: T get()
。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供
”一个符合泛型类型的对象数据。
1 | /** |
练习:求数组元素最大值:
- 使用
Supplier
接口作为方法参数类型,通过Lambda
表达式求出int
数组中的最大值。提示:接口的泛型请使用java.lang.Integer
类。
1 | /** |
Consumer接口
java.util.function.Consumer<T>
接口则正好相反,它不是生产一个数据,而是消费
一个数据,其数据类型由泛型参数决定。Consumer 接口中包含抽象方法
void accept(T t)
,意为消费一个指定泛型的数据。基本使用如:
1 | /** |
Consumer接口默认方法andThen
如果一个方法的参数和返回值全都是
Consumer
类型,那么就可以实现效果:消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是Consumer
接口中的default
方法andThen
。下面是JDK8 Consumer类
的源代码:
1 |
|
java.util.Objects 的 requireNonNull 静态方法
将会在参数为null时主动抛出NullPointerException 异常。这省去了重复编写if语句和抛出空指针异常的麻烦。- 要想实现组合,需要两个或多个Lambda表达式即可,
而 andThen 的语义正是“一步接一步”操作
。例如两个步骤组合的情况:
1 | /** |
运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。当然,通过链式写法可以实现更多步骤的组合。
练习:格式化打印信息
下面的字符串数组当中存有多条信息,请按照格式
“ 姓名:XX 性别:XX”
的格式将信息打印出来。要求将打印姓名的动作作为第一个Consumer
接口的Lambda
实例,将打印性别的动作作为第二个Consumer
接口的Lambda
实例,将两个Consumer
接口按照顺序“拼接”到一起。
1 | /** |
4. 方法引用和构造器引用
Employee
1 | public class Employee { |
4.1 方法引用
- 使用场景:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
- 方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,
可以认为是Lambda表达式的一个语法糖
。- 要求:
实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!
- 格式:使用
操作符 “::” 将类(或对象) 与 方法名分隔开来
。如下三种主要使用情况:
对象:: 实例方法名
类:: 静态方法名
类:: 实例方法名
1 | /** |
4.2 构造器引用
- 构造器引用:
格式: ClassName::new
- 与函数式接口相结合,自动与函数式接口中方法兼容。
- 可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象方法的参数列表一致!且方法的返回值即为构造器对应类的对象。
- 数组引用:
格式: type[] :: new
1 | /** |