从 java8
开始,lambda
正式在 JAVA
中出现。有人说它类似于其他函数式编程语言中的闭包,有人说它是一种语法糖。让我们来一步一步了解它。
lambda 表达式的结构
lambda 表达式的结构大致如下,同时遵循偷懒原则:能省则省,能不写就不写。
(类型)(参数1, 参数2, …) -> {方法实现}
- 类型一般情况下可以自动推导出来,此时可以省略不写。如果存在多个类型满足条件,则需指定类型。
- 可以有零个或多个参数,参数写在圆括号内。
- 没有参数时只需写
()
,只有一个参数且类型可以推导时可以不写圆括号。 - 参数的类型可以省略不写,根据上下文来推断。例如:
(int a)->{}
与(a)->{}
与a->{}
效果相同。 - 方法实现可以有零条或者多条语句,方法实现写在花括号内。
- 如果方法实现只有一条语句,则花括号可以省略。如:
a -> return a>1
。 - 如果方法实现只有一条语句,
return
关键字可以省略。如:a -> a==1
。 - 如果方法实现有多条语句,则需写在花括号内。
- 如果方法是静态方法、实例方法或者构造器,可简写。如:
(int a) -> {return String.valueOf(a)}
等效于a -> String.valueOf(a)
,也等效于String::valueOf
。
从策略模式开始演化
先给定场景:A市有一批人提交了落户申请,但是A市不像某经济特区,来了还不是A市人,要满足一定的条件才可以落户。
1 | //定义居民对象,包含姓名、年龄、社保月数、认证证书级别 |
提供初始化数据方法
1 | List<Citizen> initCitizens() { |
定义数据处理框架方法及处理接口
1 | List<Citizen> filterApply(List<Citizen> citizenList, SettleStrategy strategy) { |
策略模式
假设落户有两种方式:
- 不超过40岁且有国家认证的高于2级的证书
- 不超过40岁且累计交满12个月社保
定义策略实现类
1 | public class CertificateSettleStrategy implements SettleStrategy { |
则使用策略模式的写法大致如下:
1 | public void testStrategy() { |
点评:
从上述代码可以看出,使用策略模式时,要求必须有指定的策略类。
虽然好处是有新策略可以直接添加一个策略处理类,无需反复修改处理逻辑。
但是仍然面临着策略变化,必须增加新策略类的问题。
匿名内部类
使用匿名内部类,可无需再定义策略处理类。可能你在某些业务地方用过,比如往线程池里丢一个 Runnable
,你可能不会再单独定义一个子类。
1 | public void testAnonymous() { |
点评:
从上述代码可以看出,使用匿名内部类时,可以允许自己不再定义单独的类,但还需显式的
new
子类并override
。相比策略模式,代码更集中,也不必在项目里维护太多的类。缺点则是当处理逻辑较复杂时,写法不优雅,看起来会杂乱。
此时,比如在
intellij idea
里,应该会提示你代码可以replace with lambda
了
lambda 表达式
lambda 的写法会简洁不少,遵循上面说的偷懒原则,省略类型、省略参数、省略括号。
1 | public void testLambda() { |
点评:
相比匿名内部类,lambda 更近一步。此时,不用再出现
SettleStrategy
字样,不必再出现test
方法字样。既然如此,那么,此时就剩最后一个问题了:为什么还要定义
SettleStrategy
接口呢?
stream
stream 提供了一系列的便捷处理方法,java8 也提供了一系列的通用的函数式接口,让我们免去定义一些不必要的接口类。当然,stream 与函数式接口不在本篇的内容范围内。
通过 stream 的写法,可以解决 lambda 遗留的那个问题。
1 | public void testStream() { |
看,链式调用,是不是很简洁易读?
lambda 表达式与匿名内部类的区别
从上面的介绍可以看出,其实 lambda 表达式和匿名内部类很相似,相似到只是写法稍作改变。如果从上面所说的偷懒原则来看,lambda 表达式就是匿名实现了函数式接口,并把匿名内部类一步步的偷懒简化。但是二者从本质上来讲,又是不同的。
匿名内部类,本质还是一个类,实例化的是一个对象;lambda 表达式从根本上来说,是一种编译器会特定处理的语法。
从使用上来看,匿名内部类还是要 new 出来一个实例化对象的,而 this 关键字在匿名内部中使用时,指的是内部类,而非外部类;对于 lambda 表达式,this 关键字则代表的是外部类(lambda所在的类)。从编译输出来看,当使用匿名内部类时,会编译出一个单独的内部类的 class;而 lambda 表达式则不会编译出单独的 class 出来。
总结
本文总结了 lambda 表达式的结构,同时从策略模式说起,一步步的简化,逐步引出了 lambda 表达式,并比较不同写法之间的差异性,同时稍稍介绍了 stream。
lambda 表达式可以让我们不用去费力的给一个方法、或者一个类命名,让我们可以以匿名的方式去使用它。但是如果匿名方法里的处理逻辑太复杂,则不建议使用 lambda 表达式去写,lambda 所表达的内容应该是简单易读的。
注:本文配套代码可在
github
查看:stream-and-lambda