概念
Lambda
大写Λ
,小写λ
。读音:lan b(m) da(兰木达)['læmdə]
。Lambda
表达式Lambda expression
:基于数学中的λ
演算得名,直接对应于其中的Lambda 抽象(lambda abstraction)
,是一个匿名函数,即没有函数名的函数,在Java
中又称为闭包或匿名函数。而λ演算
是函数式编程的基础,所以Lambda
表达式具有部分函数式语言的特征。特点就是简(省)单(代)优(码)雅。
重要特性
Lambda
表达式的语法
1 | (params) -> expression |
示例:
1 | // Java 8 之前匿名内部类 |
其他示例:
- 参数可以是零个或多个
- 参数类型可指定,可省略(根据表达式上下文推断)
- 参数包含在圆括号中,用逗号分隔
- 表达式主体可以是零条或多条语句,包含在花括号中
- 表达式主体只有一条语句时,花括号可省略
- 表达式主体有一条以上语句时,表达式的返回类型与代码块的返回类型一致
- 表达式只有一条语句时,表达式的返回类型与该语句的返回类型一致
1 | // 零个 |
方法引用 Method Reference
格式:类名::方法名
。注意:只需要写方法名,不需要写括号。方法引用来简写 Lambda
表达式中已经存在的方法。示例:
1 | // Java 8 之前使用 for-each 遍历 |
方法引用的四种形式:
- 引用静态方法
ContainingClass::staticMethodName
- 引用某个类型的任意对象的实例方法
ContainingType::methodName
- 引用构造方法
ClassName::new
- 引用某个对象的实例方法
containingObject::instanceMethodName
函数式接口 FI
函数式接口:指仅含有一个抽象方法的接口。首先是一个接口,然后就是在这个接口里面只能有一个抽象方法。以 @Functionalnterface
标注,简称 FI
。但是加不加 @FunctionalInterface
对于接口是不是函数式接口没有影响,该注解只是提醒编译器去检查该接口是否仅包含一个抽象方法。
1 | // 首先是接口,其次只有一个抽象方法 |
- 引申
Java 8
中引入了新特性,向接口中引入默认方法和静态方法,以此来减少抽象类和接口之间的差异。也就是说新特性中,接口可以和抽象类一样,具有默认或静态的方法实现,如示例:
1 | 文件:java.util.function.Predicate.java |
以前如果接口中新增方法,需要修改所有的实现类增加方法的对应实现。 Java 8
的这个新特性就是主要解决这类问题的,放到接口中后,实现类不需要重写,确保老版本的代码能够兼容。
Lambda
实现函数式接口Lambda
通过该方式,大大简化了代码量,也是最基本的表达式之一。
1 | // Java 8 之前匿名内部类 |
this
关键字
Lambda
表达式即将正式取代 Java
代码中的匿名内部类,他们在关键字 this
上有很大不同:匿名类的 this
关键字指向匿名类,而 Lambda
表达式的 this
关键字指向包围 Lambda
表达式的类。
局部变量
Lambda
表达式对局部变量有个限制,那就是只能引用 final
局部变量,这就是说不能在 Lambda
表达式内部修改定义在域外的变量。示例:
1 | List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7}); |
集合的流式操作
流式操作: JDK8
的 Stream
是一个受到函数式编程和多核时代影响而产生的东西。java.util.stream
包,实现了集合的流式操作,流式操作包括集合的过滤,排序,映射等功能。根据流的操作性,又可以分为串行流和并行流。根据操作返回的结果不同,流式操作又分为中间操作和最终操作。大大方便了我们对于集合的操作:
- 最终操作:返回一特定类型的结果
- 中间操作:返回流本身
Map
和 Reduce
应用
Map
和 Reduce
操作是函数式编程的核心操作,map
将集合类(例如列表)元素进行转换,也就是将参数转换成想要的返回值;reduce
函数可以将所有值合并成一个,又被称为折叠操作。示例:
1 | // Java 8 之前,为每个订单加上12% 的税 |
过滤器 filter
过滤是在大规模集合上的一个常用操作,而使用 Lambda
表达式和流 API
过滤大规模数据集合非常简单。示例:
1 | // 创建一个字符串列表,每个字符串长度大于 2 |