重拾Kotlin(14)-Lambda表达式

一、Lambda 表达式

1.1、简介

Lambda 表达式本质上就是可以传递给其它函数的一小段代码,通过 Lambda 表达式可以把通用的代码结构抽取成库函数,也可以把 Lambda 表达式存储在一个变量中,把这个变量当做普通函数对待

Kotlin 中的 Lambda 表达式始终用花括号包围,通过箭头把实参列表和函数体分开

    val plus = { x: Int, y: Int -> x + y }
    println(plus(20, 22))   //42

1.2、语法

Lambda 表达式最常见的用途就是和集合一起工作,看以下例子

要从一个人员列表中取出年龄最大的一位

data class Person(val name: String, val age: Int)

fun main(args: Array<String>) {
    val people = listOf(Person("leavesC", 24), Person("Ye", 22))
    println(people.maxBy { it.age }) //Person(name=leavesC, age=24)
}

当中,库函数 maxBy 可以在任何集合上调用,其需要一个实参:一个函数,用于指定要用来进行比较的函数。花括号中的代码 { it.age } 就是实现了这个逻辑的 Lambda 表达式

上述 maxBy 函数的实参是简化后的写法,这里来看下 maxBy 函数的简化过程

最原始的语法声明应该是这样的,用括号包裹着 Lambda 表达式

println(people.maxBy({ p: Person -> p.age }))

Kotlin 有一种语法约定,如果 Lambda 表达式是函数调用的最后一个实参,可以将之放到括号的外边

 println(people.maxBy() { p: Person -> p.age })

当 Lamdba 表达式是函数唯一的实参时,可以去掉调用代码中的空括号对

 println(people.maxBy { p: Person -> p.age })

当 Lambda 表达式的参数类型是可以被推导出来时就可以省略声明参数类型

println(people.maxBy { p -> p.age })

如果当前上下文期待的是只有一个参数的 Lambda 表达式且参数类型可以被推断出来,就会为该参数生成一个默认名称:it

 println(people.maxBy { it.age })

1.3、在作用域中访问变量

Kotlin 和 Java 的一个显著区别就是,在 Kotlin 中函数内部的 Lambda 表达式不会仅限于访问函数的参数以及 final 变量,在 Lambda 内部也可以访问并修改非 final 变量

从 Lambda 内部访问外部变量,我们称这些变量被 Lambda 捕捉。当捕捉 final 变量时,变量值和使用这个值的 Lambda 代码一起存储,对非 final 变量来说,其值被封装在一个特殊的包装器中,对这个包装器的引用会和 Lambda 代码一起存储

    var number = 0
    val list = listOf(10, 20, 30, 40)
    list.forEach {
        if (it > 20) {
            number++
        }
    }
    println(number) //2

1.4、成员引用

成员引用用于创建一个调用单个方法或者访问单个属性的函数值,通过双冒号把类名称和要引用的成员(一个方法或者一个属性)名称分隔开

成员引用的一个用途就是:如果要当做参数传递的代码块已经被定义成了函数,此时不必专门创建一个调用该函数的 Lambda 表达式,可以直接通过成员引用的方式来传递该函数(也可以传递属性)。此外,成员引用对扩展函数一样适用

data class Person(val name: String, val age: Int) {

    val myAge = age

    fun getPersonAge() = age
}

fun Person.filterAge() = age

fun main(args: Array<String>) {
    val people = listOf(Person("leavesC", 24), Person("Ye", 22))
    println(people.maxBy { it.age })    //Person(name=leavesC, age=24)
    println(people.maxBy(Person::age))  //Person(name=leavesC, age=24)
    println(people.maxBy(Person::myAge))  //Person(name=leavesC, age=24)
    println(people.maxBy(Person::getPersonAge))  //Person(name=leavesC, age=24)
    println(people.maxBy(Person::filterAge))  //Person(name=leavesC, age=24)
}

不管引用的是函数还是属性,都不要在成员引用的名称后面加括号

此外,还可以引用顶层函数

fun test() {
    println("test")
}

fun main(args: Array<String>) {
    val t = ::test
}

也可以用构造方法引用存储或者延期执行创建类实例的动作

data class Person(val name: String, val age: Int)

fun main(args: Array<String>) {
    val createPerson = ::Person
    val person = createPerson("leavesC", 24)
    println(person)
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券