一、初识 Lambda
简化过程:
- 常规实现接口。
- 静态内部类。
- 局部内部类。
- 匿名内部类。
- lambda 表达式。
- 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 变量,可以给其赋值。

Java8 之前,这个是做不到的。Java8 之后,利用 Lambda 特性就可以做到,如图:


这样,就成功的把“一块代码”赋给了一个变量。而“这块代码”,或者说“这个被赋给一个变量的函数”,就是一个 Lambda 表达式。但是这里仍然有一个问题,就是变量 aBlockOfCode 的类型应该是什么?
在 Java8 里面,所有的 Lambda 的类型都是一个接口。而 Lambda 表达式本身,也就是“那块代码”,需要是这个接口的实现。这是理解 Lambda 的一个关键所在,简而言之,Lambda 表达式本身就是一个接口的实现。便于理解,继续往下看。给 aBlockOfCode 加上一个类型:

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


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


有些情况下,该接口实现只用到一次。Java7 要求必须定义一个“污染环境”的接口实现 MyInterfaceImpl,而相较之下 Java8 的 Lambda 就显得干净很多。
1️⃣Lambda 结合 FunctionalInterface Lib、forEach、stream()、method reference 等新特性可以使代码变的更加简洁。
假设 Person 的定义和 List<Person> 的值都给定:
要打印出 guiltyPersons 里面所有 LastName 以“Z”开头的人的 FirstName。
原生态 Lambda 写法:定义两个函数式接口,定义一个静态函数,调用静态函数并给参数赋值 Lambda 表达式。
这个代码实际上已经比较简洁了,但是还可以更简洁么?
当然可以。在 Java8 中有一个函数式接口的包,里面定义了大量可能用到的函数式接口java.util.function(Java Platform SE 8)。所以,在这里压根都不需要定义 NameChecker 和 Executor 这两个函数式接口,直接用 Java8 函数式接口包里的 Predicate<T> 和 Consumer<T> 就可以——因为这一对的接口定义和 NameChecker/Executor 其实是一样的。
第一步简化 - 利用函数式接口包:

静态函数里面的 for each 其实是非常碍眼的。这里可以利用 Iterable 自带的 forEach() 来替代。forEach() 本身可以接受一个 Consumer<T> 参数。
第二步简化 - 用 Iterable.forEach() 取代 foreach loop:

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

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

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

这基本上就是能写的最简洁的版本了。
2️⃣Lambda 配合 Optional<T> 可以使 Java 对于 null 的处理变的异常优雅。
假设有一个 person object,以及一个 person object 的 Optional wrapper:

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



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