参考文章:一文吃透 Kotlin 中目不暇接的函数家族

名词解释

函数字面值:指不声明而是直接作为表达式传递的函数,lambda表达式和匿名函数都是函数字面值

一、Lambda表达式

1.1、语法形式如下:

val doubleMax: (Int, Int) -> Int = { x, y ->
        val sum = x.coerceAtLeast(y)
        sum + sum
}
//或者 和属性相同能够省掉函数类型,自行揣度
val doubleMax = { x: Int, y: Int ->
        val sum = x.coerceAtLeast(y)
        sum + sum
}
  • 表达式总是括在花括号内
  • 函数体跟在->符号后面
  • 假如揣度出该lambda的回来类型不是Unit,那么该lambda主体中的最终一个表达式的值会视为回来值

1.2、传递末尾的lambda表达式

按照Kotlin的常规,假如函数的最终一个参数是函数,那么作为相应参数传入的lambda表达式能够放在圆括号之外:

//高阶函数--能够将函数作为参数传递的函数
fun cal(x: Int, comparator: (Int, Int) -> Int): Int {
        return x * comparator(x, 2)
}
//调用
cal(100) { x, y -> x * y }
//成果:100*100*2 = 20000

假如该lambda表达式是函数的唯一参数,那么圆括号能够省掉

fun cal(comparator: (Int, Int) -> Int): Int {
        return 100 * comparator(100, 3)
}
//调用
cal{ x, y -> x * y }
//成果:100*100*3 = 30000

1.3、it:单个参数的隐式称号

假如一个lambda表达式只要一个参数,则界说表达式时能够省掉参数名,该参数会隐式声明为it

fun cal(comparator: (Int) -> Int): Int {
        return comparator(100)
}
//调用
cal { it * it }
//等价于(it可省掉)
cal { it -> it * it }

1.4、lambda表达式回来值

能够运用限制回来语法从lambda显式回来一个值。不然,将隐式回来最终一个表达式的值

val list = mutableListOf<Int>()
list.add(100)
list.add(-100)
val list2 = list.filter {
    return@filter it > 0
}
//等价于
val list2 = list.filter {
    it > 0
}

1.5 下划线用于未运用的变量

假如lambda表达式的参数未运用,能够用下划线替代其称号

map.forEach { (_, value) -> println("$value!") }

1.6、表达式回来

在Kotlin中,只能对具名或者匿名函数运用正常的、非限制的return 来退出。要退出一个lambda表达式,需求一个标签。

在lambda表达式内部制止运用裸return ,因为lambda表达式不能使包括它的函数回来

val doubleMax = test@{ x: Int, y: Int ->
        val sum = x.coerceAtLeast(y)
        return@test sum + sum
    }
val doubleMax2 = { x: Int, y: Int ->
        val sum = x.coerceAtLeast(y)
        return sum + sum //过错
}
list.filter {
     return it > 0 //过错
}

但是假如lambda表达式是传给的函数是内联的,则return也能够内联。如下kotlin.collectionsforEach 函数是内联函数,能够这样:

list.forEach {
     sum += it
     if (sum > 100) {
        return
     }
 }

二、匿名函数

上文的lambda表达式语法缺少一个才能——指定函数的回来类型的才能。大都是情况下是不需求的,回来类型能够自动揣度出来。当然,假如确实需求显示指定,能够运用另一种语法:匿名函数

fun(x: Int, y: Int):Int = x+y
//或者
fun(x: Int, y: Int): Int {
   return x + y //return不可省掉
}

匿名函数看起来非常像一个常规函数声明,除了其称号省掉了。

匿名函数的回来类型揣度机制和正常函数相同:单表达式函数体的匿名函数将自动揣度回来类型,但是具有代码块函数体的回来类型有必要显式指定(Unit回来类型不需求指定)

三、Function接口

Kotlin decompile转换成Java文件后,能够看出lambda表达式和匿名函数本质上对应的是Function接口。lambda表达式和匿名函数会被转换成一个个Function 接口对象

package kotlin.jvm.functions
/** A function that takes 0 arguments. */
public interface Function0<out R> : Function<R> {
    /** Invokes the function. */
    public operator fun invoke(): R
}
/** A function that takes 1 argument. */
public interface Function1<in P1, out R> : Function<R> {
    /** Invokes the function with the specified argument. */
    public operator fun invoke(p1: P1): R
}
/** A function that takes 2 arguments. */
public interface Function2<in P1, in P2, out R> : Function<R> {
    /** Invokes the function with the specified arguments. */
    public operator fun invoke(p1: P1, p2: P2): R
}
...
public interface Function22<...> : Function<R> {
    /** Invokes the function with the specified arguments. */
    public operator fun invoke(...): R
}

最多支撑22个参数,当然咱们也能够自己扩展

四、闭包

闭包概念:参考文章

函数和对其周围状态(lexical environment,词法环境)的引用绑缚在一起构成闭包。

周围环境应该怎么了解呢,能够了解为函数所在的外部效果域中界说的各个自由变量,这个效果域可能是另一个函数或块级效果域。二者绑缚在一起构成的闭包使得函数内部能够对外部效果域界说的变量进行拜访。

Lambda表达式或者匿名函数(以及部分函数)能够拜访闭包,即在外部效果域中声明的变量。 在 lambda 表达式中能够修改闭包中捕获的变量。示例:

fun main() {
    val list = mutableListOf<Int>()
    list.add(2)
    list.add(3)
    list.add(-1)
    var sum = 0
    list.filter { it > 0 }.forEach {
    //这儿能够拜访sum属性
             sum += it
    }
}