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

什么是闭包闭包的英文对应的是Closure,如果要单纯的讨论这个概念的话就要提到和图灵机起名的大名鼎鼎的lambda演算(lamdba calculus) 。尽管lamdba的概念并不是本文的重点,但是闭包概念的目的便是支持lamdba的实现 。如果你单独地在百度对进行搜索闭包的话,你会发现大部分都是js相关的内容,主要是js本身就只用闭包的这个概念 。但是闭包并不仅限于js,而是一个通用的概念 。借用wiki中有点抽象的定义来说的话,闭包就是:
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数 。
【为什么需要闭包 为什么需要闭包,以及优缺点】简单来说就是当一个方法引用了方法局部变量外的变量时,它就是一个闭包 。而如果根据这个定义继续延展的话,就可以得到另外的一种描述方法:
闭包是由函数和与其相关的引用环境(方法外变量)组合而成的实体 。
为什么需要闭包尽管给出了两种关于闭包是什么的定义,但是闭包本身的概念还是较为的抽象 。比如直接给例子,我们可以先讨论一下为什么需要闭包 。
为了理解闭包,我们可以先假设这样的一种场景:
我有一个方法A,其中有私有变量 。我想在别的方法中可以直接访问并使用它,但是又不想直接暴露它 。
这时候,就可以在A建立一个内部类B来访问私有变量 。那么这时候,这个内部类中你所可以访问B的方法,就和A中的私有变量形成了闭包 。此时B已经捕获了A中的变量 。即便是A对象销毁了,被B捕获的私有变量仍然不会释放 。
所以可以理解,如果希望系统方法和当前环境的上下文绑定的话,就可以使用闭包 。
尽管有些不恰当,但是我们可以再换一个实际的场景来举个例子帮助你理解:
当你在购物的时候,你选中了一件商品,然后下单 。这时候你的订单就已经绑定了你选中的商品 。那么你的购买行为就和商品组成了闭包 。这时候即便商家下架商品,也不会影响到商品后续的发货签收 。并且这些逻辑只需要根据购买行为中的信息进行判断就可以了,而不用关心商家什么时候把商品下架,这写逻辑发生时候的商品价格是什么 。
结合上面的例子,我们可以发现,当一个方法和其调用的外部环境形成闭包的时候,由于外部环境已经确定,那么我们就不用再关心外部环境了,而只用关心方法本身 。
所以针对我们为什么需要闭包,我给出的答案是:
使用闭包的设计方式,由于闭包本身已经包含了上下文信息,所以可以对北向功能调用(用户)屏蔽由于环境而引发的复杂处理和交互成本 。
Java中的闭包对于Java来说,可以理解为主要是两种的闭包方式:

  • 内部类
  • lambda表达式
其中内部类除了本身的内部类还有局部内部类、匿名内部类 。我们以最简洁的匿名内部类来举例:
public class Closure {private int length = 0;public ISay count() {return new ISay() {@Overridepublic void say(String message) {length = message.length();return "Talk:" + length;}};}}
此时say方法中便捕获了length属性,而如果你使用的是足够版本的IDE的话,获取还会提示你:
Anonymous new ISay() can be replaced with lambda
替换后:
public class Closure {private int length = 0;public ISay count() {return message -> {length = message.length();return "Talk:" + length;};}}
那么这时候你就会发现,此时return的对象就是一个通过匿名后直接描述的函数,而这个函数同时还关联了上下文之外的环境信息 。
而在java8中lambda的中我们可以通过函数式编程接口直接接收这种闭包,这些接口常用的为:
  • Supplier
  • Predicate
  • Function
关于函数式接口本文就不展开了,但是利用函数式接口,我们就可以这样传递一个闭包:
public class Closure {private int length = 0;public Function say() {return message -> {length = message.length();return "Talk:" + length;};}}
此时say返回的就不是String的结果了,而是直接将闭包本身返回了 。而这个闭包在创建的时候就已经绑定了所需要的环境属性 。所以我们可以在需要的时候再调用闭包,并且不用再关心它到底依赖了其他哪些变量:
public static void main(String[] args) {final Function say = new Closure().say();System.out.println(say.apply("Something."));}
当然你可能会角色,这个length似乎没有什么用呀,那如果我们换一个形式:
@Servicepublic class Closure {@Resourceprivate HowtoSay howToSay;public Function say() {return message -> {return "Talk:" + howToSay.say(message);};}}