简介
lambda 表达式, 又称闭包 (Closure) 或称匿名方法(anonymous method). 将 Lambda 表达式引入 JAVA 中的动机源于一个叫 "行为参数" 的模式. 这种模式能够解决需求变化带来的问题, 使代码变得更加灵活. 在 JAVA8 之前, 参数模式十分啰嗦. Lambda 表达式通过精简的方式使用行为模式克服了这个缺点
解决什么问题
传递行为. 它允许我们将函数当成参数传递给某个方法, 或者把代码本身当作数据处理, 变成了一等公民. 解决重复的代码片段和代码包裹问题.
内置抽象行为. 把常见的行为定义成接口, 可以直接使用,
减少重复思考和代码
. 都在 java.util.function 包里
更少的代码. 通过类型推断, 方法引用, 可以让代码更优雅.
背后思想
函数式编程
内容说明
作用域
this
在内部类中, this 指向当前内部类对象自己, 而在 lambda 表达式中, this 指向的是表达式外部的类对象.
- public class ScopeTest {
- @Test
- public void test_scope(){
- Runnable runnable = () -> {
- this.print();
- };
- runnable.run();
- }
- private void print(){
- System.out.println("I can print");
- }
- }
- final
labmda 表达式使用外部的变量时, 不可修改, 默认定义成 final
函数式接口
只有一个抽象方法的接口我们就称之为功能性接口, 又简称 SAM 类型, 即 Simple Abstract Method. 会写上注释
- @FunctionalInterface
- // 用这个注解来表示功能性接口
- public interface Consumer<T> {
- /**
- * Performs this operation on the given argument.
- *
- * @param t the input argument
- */
- void accept(T t);
- }
常见的内置函数如下:
name | function |
---|---|
java.lang.Runnable | 执行动作 |
java.util.function.Predicate <T> | 接收 T 对象并返回 boolean |
java.util.function.Consumer<T> | 接收 T 对象,不返回值 |
java.util.function.Function<T,R> | 接收 T 对象,返回 R 对象 |
java.util.function.Supplier<T> | 提供 T 对象(例如工厂),不接收值 |
方法引用
方法引用有很多种, 它们的语法如下:
静态方法引用: ClassName::methodName
实例上的实例方法引用: instanceReference::methodName
超类上的实例方法引用: super::methodName
类型上的实例方法引用: ClassName::methodName
构造方法引用: Class::new
数组构造方法引用: TypeName[]::new
- @Test
- public void test_instance(){
- Set<String> girls = new HashSet<>();
- Set<String> names = new HashSet<>();
- names.stream()
- // 实例::methodName
- .filter(girls::contains)
- .collect(Collectors.toList());
- }
- @Test
- public void test_this(){
- Set<String> names = new HashSet<>();
- names.stream()
- //this::methodName
- .filter(this::hasAuth)
- .collect(Collectors.toList());
- }
- private boolean hasAuth(String authKey){
- return true;
- }
类型推断
- Map<String,Person> map = new HashMap<>();
- map.forEach((String name,Person person)->{
- person.fly();
- System.out.println(name+":"+person);
- });
- map.forEach((name,person)->{
- // 无须判断类型, 自动根据上下文推断
- person.fly();
- System.out.println(name+":"+person);
- });
实践
最佳实践
消灭代码片段
- /**
- * 正常的代码
- */
- @Test
- public void test_person(){
- CnResult<Person> result = null;
- try {
- // 只有这里取值是不同的, 其他的处理是一样的, 包括 try,catch, 打日志, 定义异常等
- Person entity = this.getPerson();
- result = CnResult.success(entity);
- }catch (CnException e){
- logger.error(e.getMessage(),e);
- result = CnResult.error(e.getErrorCode(),e.getMessage());
- }
- catch (Exception e) {
- logger.error(e.getMessage(),e);
- result = CnResult.error("1-1-1-1",e.getMessage());
- }
- Assert.assertNotNull(result);
- }
- @Test
- public void test_animal(){
- CnResult<Animal> result = null;
- try {
- // 只有这里取值是不同的, 其他的处理是一样的, 包括 try,catch, 打日志, 定义异常等
- Animal entity = this.getAnimal();
- result = CnResult.success(entity);
- }catch (CnException e){
- logger.error(e.getMessage(),e);
- result = CnResult.error(e.getErrorCode(),e.getMessage());
- }
- catch (Exception e) {
- logger.error(e.getMessage(),e);
- result = CnResult.error("1-1-1-1",e.getMessage());
- }
- Assert.assertNotNull(result);
- }
- /**
- * lambda 代码
- */
- @Test
- public void test_lambda(){
- // 屏蔽所有细节, 只把获取对象的逻辑传递进去
- CnResult<Person> person = WapperUtils.wapper(this::getPerson);
- Assert.assertNotNull(person);
- CnResult<Animal> animal = WapperUtils.wapper(this::getAnimal);
- Assert.assertNotNull(animal);
- }
- public class WapperUtils {
- private static final Logger logger = LoggerFactory.getLogger(WapperUtils.class);
- /**
- * 包裹
- *
- * @param supplier
- * @param <T>
- * @return
- */
- public static <T> CnResult<T> wapper(Supplier<T> supplier){
- try {
- T entity = supplier.get();
- CnResult<T> cnResult = CnResult.success(entity);
- return cnResult;
- }catch (CnException e){
- logger.error(e.getMessage(),e);
- return CnResult.error(e.getErrorCode(),e.getMessage());
- }
- catch (Exception e) {
- logger.error(e.getMessage(),e);
- return CnResult.error("1-1-1-1",e.getMessage());
- }
- }
- }
只要是代码片段, 找到变点化, 抽象成 lambda, 把固定的代码统一封装, 就可以使用行为的复用, 减少代码片段和代码包裹.
明确语义
- @Test
- public void test_first() {
- List<Person> persons = new ArrayList<>();
- persons.stream()
- /**
- * 没有明确的语义. 需要根据过程计算推理得出结果, 也不清楚背后的业务和场景
- *
- */
- .filter(person -> person.getAge()>= 18)
- .collect(Collectors.toList());
- }
- @Test
- public void test_second() {
- List<Person> persons = new ArrayList<>();
- persons.stream()
- .filter(person -> {
- /**
- * 如果职责变更, 得修改代码结构, 而且代码越来越难理解
- */
- if (person.getAge()>= 18) {
- return true;
- }
- if (person.getName().startsWith("庄")) {
- return true;
- }
- return false;
- })
- .collect(Collectors.toList());
- }
- @Test
- public void test_third() {
- List<Person> persons = new ArrayList<>();
- persons.stream()
- /**
- * 随着业务变更需要不断添加 filter
- */
- .filter(person -> person.getAge()>= 18)
- .filter(person -> person.getName().startsWith("庄"))
- .collect(Collectors.toList());
- }
- @Test
- public void test_fourth() {
- List<Person> persons = new ArrayList<>();
- persons.stream()
- /**
- * 随着业务变更需要不断添加 filter
- */
- .filter(Person::isAdult)
- .filter(Person::belongToZhuang)
- .collect(Collectors.toList());
- }
- @Test
- public void test_final() {
- List<Person> persons = new ArrayList<>();
- /**
- * 有明确的语义, 不用再面向细节加工一下, 而且也方便后面的扩展
- */
- persons.stream()
- .filter(Person::hasAuth)
- .collect(Collectors.toList());
- }
最佳反例
随意取名
- @Test
- public void test_name_bad(){
- List<Person> persons = new ArrayList<>();
- /**
- * lambda 一多的时候, 没有明确的参数, 计算和理解起来非常吃力.
- */
- persons.stream()
- .filter(e->e.getAge()>18)
- .map(e->e.getName())
- .filter(e->e.startsWith("庄"))
- .collect(Collectors.toList());
- }
- @Test
- public void test_name(){
- List<Person> persons = new ArrayList<>();
- persons.stream()
- .filter(person->person.getAge()>18)
- .map(person->person.getName())
- .filter(name->name.startsWith("庄"))
- .collect(Collectors.toList());
- }
参数需要有明确语义
逻辑复杂
- @Test
- public void test_bad() {
- List<Integer> values = new ArrayList<>();
- int result = values.stream().mapToInt(e -> {
- int sum = 0;
- for (int i = 0; i <e; i++) {
- if (e % i == 0) {
- sum += i;
- }
- }
- return sum;
- }).sum();
- System.out.println(result);
- }
- @Test
- public void test_() {
- List<Integer> values = new ArrayList<>();
- int result = values.stream().mapToInt(this::toInt).sum();
- System.out.println(result);
- }
- private Integer toInt(int e) {
- int sum = 0;
- for (int i = 0; i < e; i++) {
- if (e % i == 0) {
- sum += i;
- }
- }
- return sum;
- }
难以读懂
用途不明
难以测试
难以复用
建议把多行 lambda 表达式主体转移到一个命名函数中, 然后使用方法引用
思考
和内部类的区别
和 AOP 的区别
来源: https://www.cnblogs.com/ansn001/p/9399360.html