Lambda表达式和匿名函数-Kotlin

参考文章:一文吃透 Kotlin 中眼花缭乱的函数家族

名词解释

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

一、Lambda表达式

1.1、语法形式如下:

Kotlin 复制代码
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表达式可以放在圆括号之外:

Kotlin 复制代码
//高阶函数--可以将函数作为参数传递的函数
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表达式是函数的唯一参数,那么圆括号可以省略

Kotlin 复制代码
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

Kotlin 复制代码
fun cal(comparator: (Int) -> Int): Int {
        return comparator(100)
}
//调用
cal { it * it }
//等价于(it可省略)
cal { it -> it * it }

1.4、lambda表达式返回值

可以使用限定返回语法从lambda显式返回一个值。否则,将隐式返回最后一个表达式的值

Kotlin 复制代码
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表达式的参数未使用,可以用下划线取代其名称

Kotlin 复制代码
map.forEach { (_, value) -> println("$value!") }

1.6、表达式返回

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

在lambda表达式内部禁止使用裸return ,因为lambda表达式不能使包含它的函数返回

Kotlin 复制代码
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 函数是内联函数,可以这样:

Kotlin 复制代码
list.forEach {
     sum += it
     if (sum > 100) {
        return
     }
 }

二、匿名函数

上文的lambda表达式语法缺少一个能力------指定函数的返回类型的能力。大都是情况下是不需要的,返回类型可以自动推断出来。当然,如果确实需要显示指定,可以使用另一种语法:匿名函数

Kotlin 复制代码
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 接口对象

Kotlin 复制代码
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 表达式中可以修改闭包中捕获的变量。示例:

Kotlin 复制代码
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
    }

}
相关推荐
吃汉堡吃到饱1 小时前
【Android】浅析MVC与MVP
android·mvc
深海呐8 小时前
Android AlertDialog圆角背景不生效的问题
android
ljl_jiaLiang8 小时前
android10 系统定制:增加应用使用数据埋点,应用使用时长统计
android·系统定制
花花鱼8 小时前
android 删除系统原有的debug.keystore,系统运行的时候,重新生成新的debug.keystore,来完成App的运行。
android
落落落sss9 小时前
sharding-jdbc分库分表
android·java·开发语言·数据库·servlet·oracle
一丝晨光10 小时前
逻辑运算符
java·c++·python·kotlin·c#·c·逻辑运算符
消失的旧时光-194311 小时前
kotlin的密封类
android·开发语言·kotlin
服装学院的IT男12 小时前
【Android 13源码分析】WindowContainer窗口层级-4-Layer树
android
CCTV果冻爽14 小时前
Android 源码集成可卸载 APP
android
码农明明14 小时前
Android源码分析:从源头分析View事件的传递
android·操作系统·源码阅读