public class ZipCompressionStrategy implements CompressionStrategy {
 @Override public void compress(List files) {
 // Do ZIP stuff
 }
 }
 public class RarCompressionStrategy implements CompressionStrategy {
 @Override public void compress(List files) {
 // Do RAR stuff
 }
 }

在运行时,我们就可以使用任意一种策略:

public CompressionStrategy decideStrategy(Strategy strategy) {
 switch (strategy) {
 case ZIP:
 return new ZipCompressionStrategy();
 case RAR:
 return new RarCompressionStrategy();
 }
 }

使用这种方式有一堆的代码和需要遵循的格式。

其实我们所要做的只是根据不同的变量实现两种不同的业务逻辑。由于业务逻辑不能在 Java 中独立存在,所以必须用类和接口去修饰。

如果能够直接传递业务逻辑,那不是很好吗?也就是说,如果可以把函数当作变量来处理,那么能否像传递变量和数据一样轻松地传递业务逻辑?

正是高阶函数的功能!

现在,从高阶函数的角度来看这同一个例子。这里我要使用 Kotlin ,因为 Java 8 的 lambdas 表达式仍然包含了我们想要避免的 一些创建函数接口的方式 。

fun compress(files: List, applyStrategy: (List) -> CompressedFiles){
 applyStrategy(files)
 }

compress 方法接受两个参数 —— 一个文件列表和一个类型为 List<File> -> CompressedFilesapplyStrategy 函数。也就是说,它是一个函数,它接受一个文件列表并返回 CompressedFiles

现在,我们调用 compress 时,传入的参数可以是任意


接收文件列表并返回压缩文件的函数。:

compress(fileList, {files -> // ZIP it})
 compress(fileList, {files -> // RAR it})

这样代码看起来干净多了。

所以高阶函数允许我们传递逻辑并将代码当作数据处理。

闭包

闭包是可以捕捉其环境的函数。让我们通过一个例子来理解这个概念。假设给一个 view 设置了一个 click listener,在其方法内部想要打印一些值:

int x = 5;
view.setOnClickListener(new View.OnClickListener() {
 @Override public void onClick(View v) {
 System.out.println(x);
 }
 });

Java 里面不允许我们这样做,因为 x 不是 final 的。在 Java 里 x 必须声明为 final,由于 click listener 可能在任意时间执行, 当它执行时 x 可能已经不存在或者值已经被改变,所以在 Java 里 x 必须声明为 final。Java 强制我们把这个变量声明为 final,实际上是为了把它设置成不可变的。

一旦它是不可变的,Java 就知道不管 click listener 什么时候执行,x 都等于 5。这样的系统并不完美,因为 x 可以指向一个列表,尽管列表的引用是不可变的,其中的值却可以被修改.

Java 没有一个机制可以让函数去捕捉和响应超过它作用域的变量。Java 函数不能捕捉或者涵盖到它们环境的变化。

让我们尝试在 Kotlin 中做相同的事。我们甚至不需要匿名内部类,因为在 Kotlin 中函数是「一等公民」:

var x = 5
view.setOnClickListener { println(x) }

这在 Kotlin 中是完全有效的。Kotlin 中的函数都是闭包。他们可以跟踪和响应其环境中的更新。

第一次触发 click listener 时, 会打印 5。如果我们改变 x 的值比如令 x = 9,再次触发 click listener ,这次会打印9

我们能利用闭包做什么?

闭包有很多非常好的用例。无论何时,只要你想让业务逻辑响应环境中的状态变化,那就可以使用闭包。

假设你在一个按钮上设置了点击 listener, 点击按钮会弹出对话框向用户显示一组消息。如果没有闭包,则每次消息更改时都必须使用新的消息列表并且初始化新的 listener。

有了闭包,你可以在某个地方存储消息列表并把列表的引用传递给 listener,就像我们上面做的一样,这个 listener 就会一直展示最新的消息。

**闭包也可以用来彻底替换对象。**这种用法经常出现在函数式编程语言的编程实践中,在那里你可能需要用到一些 OOP(面向对象编程)的编程方法,但是所使用的语言并不支持。

我们来看个例子:

class Dog {
 private var weight: Int = 10fun eat(food: Int) {
 weight += food
 }fun workout(intensity: Int) {
 weight -= intensity
 t: Int = 10fun eat(food: Int) {
 weight += food
 }fun workout(intensity: Int) {
 weight -= intensity