Kotlin 中的高阶函数

首先我们要明确,Kotlin functions are first-class, 这句话是什么意思呢?

知乎上一个比较好的回答是

函数是第一类对象,第一类值或对象要满足下列特征:

  • 可以被存放在变量和数据结构中
  • 可以当做参数传递给子过程
  • 可以当子过程的返回值
  • 可以在运行期间被创建

一个对象(广义概念上)在运行期间被创建,可以当做一个参数被传递,从子函数返回或者可以被赋给一个变量。熟悉了这个概念就可以了解下闭包了。

PS: 下文不对「函数」和「方法」做具体区分,你可以认为它俩表达的是同一个意思(但狭义上确实不是)。

举个直观的例子,我现在定义一个点击事件的回调:

// Java
// mListener 是一个 interface ,需要有对应的具体的类实现
private View.OnClickListener mListener = null;

// Kotlin
// listener 是一个输入参数为 View 实例,没有返回值的方法
private var listener: ((view: View) -> Unit)? = null

两者对比而言,Java 需要通过 interface 包一层来把某个方法赋值给变量,而 Kotlin 则没有这层限制,直接写方法即可。是不是包一层这个区别非常重要,如果是包一层,无论你是定义也好,作为返回值也好,动态创建也好(比如新建匿名内部类),都没把函数作为第一类对象来对待。

那么函数作为一等公民(第一类对象)好处都有啥,谁说对了就给他!

最本质的回答是,将各个函数组合起来更方便了。举个例子,在 Java 中定义一个 y(x)=f(g(x)) 的函数,你需要先自己写一个 g(x) 的方法,再写一个 f(x) 的方法,然后把 g(x) 传入 f(x) 得到 y(x). 如果 y(x), f(x), g(x) 都定一个在同一个类里面,看起来倒也还好,但你要知道 this 是隐式存在的:

this.y(x) = this.f(this.g(x));

但是如果它们分布在不同的类中,你在调用方法时就会显示地观察到,你的方法是依附于类而存在的,你得先调用类,才能调用到方法:

C.y(x) = A.f(B.g(x));

如果你要定义很多 y(x)=f(g(x)) 这样的方法,或者更复杂的组合方法,你会发现如果能直接操作函数是多么方便的一件事。如果你从 Java 7 开始就使用 RxJava, 你一定每次调用操作符,都要 new 一个匿名内部类;当然到 Java 8 支持 Lambda 表达式以后看起来简单很多,而 Lambda 表达式也正是函数作为一等公民带来的好处之一(这里不是说 Java 8 就把函数作为一等公民了哈,而是说 Java 正在部分吸收这一个概念到自己新的语法中,方便开发者而已。

当然像 y(x) 这种由多个函数组合而成的新函数,就称为高阶函数