button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        System.out.println("clicked");
    }
});

以上这段代码在 Java 8 中可以这么写:

button.setOnClickListener(v -> System.out.println("clicked"));

在 Kotlin 中可以这么写:

button.setOnClickListener { println("clicked") }

如何理解这段代码,一步一步来简化。

1. 匿名内部类

要创建一个继承自某类型的匿名类的对象,写法如下(这段代码相当于 Java 8 之前的匿名内部类写法):

button.setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View?) {
        println("clicked")
    }
})
2. 使用 lambda 表达式

约定如下:

  • lambda 表达式总是括在花括号中;
  • 其参数(如果有的话)在 -> 之前声明;
  • 函数体(如果存在的话)在 -> 后面。
button.setOnClickListener({ v: View? -> println("clicked") })
3. 省略参数类型

(参数类型可以省略)

省略类型,形式参数 v 没有使用时可以简写为 _

button.setOnClickListener({ _ -> println("clicked") })
4. 省略参数

如果函数字面值只有一个参数, 那么它的声明可以省略(连同 ->),其名称是 it:

button.setOnClickListener({ println("clicked") })
5. 将花括号移出圆括号

在 Kotlin 中有一个约定,如果函数的最后一个参数是一个函数,并且你传递一个 lambda 表达式作为相应的参数,你可以在圆括号之外指定它。如果 lambda 是该调用的唯一参数,则调用中的圆括号可以完全省略:

button.setOnClickListener { println("clicked") }

一个 lambda 表达式或匿名函数是一个“函数字面值”,即一个未声明的函数,但立即做为表达式传递。接受一个函数作为参数的函数是一个函数。对于接受另一个函数作为参数的函数,我们必须为该参数指定函数类型。

setOnClickListener 的函数类型为

比如:

fun captureScreenshot(view: View, viewToBitmap: (View) -> Bitmap) {
    val header = createHeader()
    val content = viewToBitmap(view)
    val footer = createFooter()
    saveBitmap(header, content, footer)
}

其中 (View) -> Bitmap 就是参数 viewToBitmap 的函数类型,它接受一个 View 返回一个 Bitmap,相当于:

什么是 lambda?简单的说就是匿名函数,它是一个“函数字面值”,即一个未声明的函数,但立即做为表达式传递。比如:

val sum = { x: Int, y: Int -> x + y }

// 使用时:
val result = sum(1, 2)
// 或者:
val result = sum.invoke(1, 2)

它相当于:

fun sum(x: Int, y: Int): Int {
    return x + y
}

// 使用时:
val result = sum(1, 2)
// 或者:
val result = ::sum.invoke(1, 2)

标签

Lambda 表达式的完整语法形式,即函数类型的字面值如下: