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
    }

}
相关推荐
Jerry说前后端3 分钟前
Android 数据可视化开发:从技术选型到性能优化
android·信息可视化·性能优化
Meteors.1 小时前
Android约束布局(ConstraintLayout)常用属性
android
alexhilton2 小时前
玩转Shader之学会如何变形画布
android·kotlin·android jetpack
whysqwhw6 小时前
安卓图片性能优化技巧
android
风往哪边走6 小时前
自定义底部筛选弹框
android
Yyyy4827 小时前
MyCAT基础概念
android
Android轮子哥7 小时前
尝试解决 Android 适配最后一公里
android
雨白8 小时前
OkHttp 源码解析:enqueue 非同步流程与 Dispatcher 调度
android
风往哪边走9 小时前
自定义仿日历组件弹框
android
没有了遇见9 小时前
Android 外接 U 盘开发实战:从权限到文件复制
android