为什么需要闭包 为什么需要闭包,以及优缺点( 二 )


使用Spring的依赖注入后,那么这个Closure类本身可能是各种策略模式策略器中的一个,策略器返回的是一个已经关联了具体策略路由的闭包 。而当这个方法提供出去的时候,后续的调用者只需要知道这个闭包是可以针对文本进行处理的就可以,而至于之前是使用的什么策略它则不用关心 。因为这些细节都已经通过闭包屏蔽了 。
闭包的问题仅仅谈论好的而对问题闭口不谈确实不好,虽然闭包提供了强大的功能,可以对业务细节进行屏蔽,对系统进行接耦拆分 。但是闭包本身确实有一些问题需要留意:

  • 让对象的生命周期变长
  • 逻辑抽象
可以发现,这两个问题都是由于闭包本身的优点而产生的 。由于闭包关联了环境信息,所以其让环境信息中对象的生命周期变长,这对于系统性能的维护以及jvm的垃圾回收都有负面因素 。而同时因为不同于一般的编码风格,闭包的使用需要开发人员对实体进行抽象,才能比较好地实现 。总结来说,对于开发人员本身有一定要求 。
加餐平时我们也经常使用lambda表达式来处理一些业务逻辑,偶尔会出现一下的情况:
interface Action{String doAction(String id);}public List doAction(List userIds){List result = new ArrayList<>();for (int i = 0; i < 10; i++) {result.add( id -> userIds.get(i)+"#");}return result;}
先不管这段代码的实现业务背景是什么,但是IDE会提示在userIds.get(i)中的i提示的信息为:
Variable used in lambda expression should be final or effectively final
结合了上文中关于闭包的内容,我们就不难理解 。由于闭包是要关联外部环境变量,在这部分代码中关联的是索引变量i,但是因为i本身是局部变量,无法保证关联环境的稳定性(我自己的理解),所以java编译器会强制的要求当闭包关联的是局部变量的时候,需要添加final关键字,而在进行final关键字从而保证该变量的内存引用不会发生改变 。从本章的代码上结论上看则是进行了一次内存拷贝,来保证每个闭包中关联的环境变量不会改变,修改后的代码为:
public List doAction(List userIds){List result = new ArrayList<>();for (int i = 0; i < 10; i++) {int finalI = i;result.add(id -> userIds.get(finalI)+"#");}return result;}
最后闭包本身是一种面向抽象编程,屏蔽细节的设计原则 。在良好的设计下,可以通过闭包来屏蔽对于环境信息的感知,从而简化外部对于系统理解的成本,提高系统的易用性 。