理解 Lambda 表达式

Java8 Stream 相关集合转换

一、初识 Lambda

简化过程:

  1. 常规实现接口。
  2. 静态内部类。
  3. 局部内部类。
  4. 匿名内部类。
  5. lambda 表达式。
  6. lambda 表达式简化。
public class TestMain {

    static class StaStudent implements Person {
        @Override
        public void speak(String language) {
            System.out.println("静态内部类" + language);
        }
    }

    public static void main(String[] args) {
        Person p = new GloStudent();
        p.speak("lan");

        p = new StaStudent();
        p.speak("lan");

        class LocStudent implements Person {
            @Override
            public void speak(String language) {
                System.out.println("局部内部类" + language);
            }
        }
        p = new LocStudent();
        p.speak("lan");

        p = new Person() {
            @Override
            public void speak(String language) {
                System.out.println("匿名内部类" + language);
            }
        };
        p.speak("lan");

        p = (language) -> {
            System.out.println("lambda 表达式---块代码1" + language);
            System.out.println("lambda 表达式---块代码2" + language);
        };
        p.speak("lan");

        //接口是函数式接口才能转换为lambda表达式
        //在只有一行代码的情况下可以将块简化为行
        //单个参数可以去掉参数类型,括号可选
        //多个参数可以同时去掉参数类型,必须括号包裹
        p = (language) -> System.out.println("lambda 表达式---单行代码" + language);
        p.speak("lan");

    }
}

class GloStudent implements Person {
    @Override
    public void speak(String language) {
        System.out.println("外部类" + language);
    }
}

interface Person {
    void speak(String language);
}

二、什么是 Lambda

对于 Java 变量,可以给其赋值。

那么如何把“一块代码”赋给 Java 变量呢?如图把右边代码,赋给左边的 Java 变量 aBlockOfCode:

Java8之后可以利用 Lambda 特性做到,如图:

当然,该写法并不简洁。为使该赋值操作更加简洁,可移除一些无用的声明,如下:

如此就成功的把“一块代码”赋给了一个变量。而“这块代码”,或者说“这个被赋给一个变量的函数”,就是一个 Lambda 表达式。但是这里有个问题,就是变量 aBlockOfCode 的类型是什么?

Java8 中所有的 Lambda 的类型都是一个接口。而 Lambda 表达式本身,也就是“那块代码”,需要是该接口的实现。这是理解 Lambda 的关键所在,简言之,Lambda 表达式本身就是一个接口的实现。便于理解,给 aBlockOfCode 加上一个类型:

这种只有一个函数需要被实现的接口类型,为“函数式接口”。为避免后人在该接口中增加接口函数导致其有多个接口方法需要被实现,变成"非函数接口”,可以在上面加上一个声明@FunctionalInterface,如此就无法在里面添加新的接口函数了:

如此就得到了一个完整的 Lambda 表达式声明:

三、Lambda 表达式:最直观的作用就是使得代码变得异常简洁。

对比一下 Lambda 表达式和传统的 Java 对同一个接口的实现:

两种写法本质上是等价的。但是 Java8 的写法更加优雅简洁。并且由于 Lambda 可以直接赋值给一个变量,就可以直接把 Lambda 作为参数传给函数,而传统的 Java 必须有明确的接口实现的定义,初始化才行:

Java7 要求必须定义一个“污染环境”的接口实现 MyInterfaceImpl。有时,该接口实现只用到一次。相较之下 Java8 的 Lambda 就显得干净很多。

四、Lambda 实践

1️⃣Lambda 结合 FunctionalInterface Lib、forEach、stream()、method reference 等新特性可以使代码变的更加简洁。

假设 Person 的定义和 List 的值都给定:

要打印出 guiltyPersons 里面所有 LastName 以“Z”开头的人的 firstName。

原生态 Lambda 写法:定义两个函数式接口,定义一个静态函数,调用静态函数并给参数赋值 Lambda 表达式。

这个代码实际上已经比较简洁了,但是还可以更简洁么?

当然可以。在 Java8 中有一个函数式接口的包java.util.function(Java Platform SE 8),里面定义了大量可能用到的函数式接口。所以,在这里压根都不需要定义 NameChecker 和 Executor 这两个函数式接口,直接用 Java8 函数式接口包里的 Predicate 和 Consumer 就可以——因为这一对的接口定义和 NameChecker/Executor 其实是一样的。

第一步简化 - 利用函数式接口包:

静态函数里面的 for each 其实是非常碍眼的。这里可以利用 Iterable 自带的 forEach() 来替代。forEach() 本身可以接受一个 Consumer 参数。

第二步简化 - 用 Iterable.forEach() 取代 foreach loop:

由于静态函数其实只是对 List 进行了一通操作,这里可以甩掉静态函数,直接使用 stream() 特性来完成。stream() 的几个方法都是接受 Predicate,Consumer 等参数的java.util.stream(Java Platform SE 8)。理解了上面的内容,stream() 就非常好理解了。

第三步简化 - 利用 stream() 替代静态函数:

对比最开始的 Lambda 写法,这里已经非常简洁了。但是,如果要求变成 print 这个人的全部信息,及 p -> System.out.println§; 那么还可以利用 Method reference 来继续简化。所谓 Method reference 就是用已经写好的别的 Object/Class 的method来代替 Lambda expression。格式如下:

第四步简化 - 如果是 println§,则可以利用 Method reference 代替 forEach 中的 Lambda 表达式:

这基本上就是能写的最简洁的版本了。

2️⃣Lambda 配合 Optional 可以使 Java 对于 null 的处理变的异常优雅。

假设有一个 person object,以及一个 person object 的 Optional wrapper:

Optional 如果不结合 Lambda 使用的话,并不能使原来繁琐的 null check 变的简单。

只有当 Optional 结合 Lambda 一起使用的时候,才能发挥出其真正的威力。现在就来对比一下下面四种常见的 null 处理中,Java8 的 Lambda+Optional 和传统 Java 两者之间对于 null 的处理差异。

情况一 - 存在则开干

情况二 - 存在则返回,不存在则自定义返回

情况三 - 存在则返回,无则由函数产生

情况四 - 夺命连环 null 检查

由上述四种情况可以清楚地看到,Optional+Lambda 可以少写很多 if/else。尤其是对于情况四,传统 Java 的写法显得冗长难懂,而新的 Optional+Lambda 则清楚简洁。

阅读终点,创作起航,您可以撰写心得或摘录文章要点写篇博文。去创作
  • 9
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JFS_Study

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值