概念
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 |