Kotlin系列之Lambda表达式(1)

今天开始后续的几篇Kotlin的文章会介绍Kotlin中Lambda表达式相关的内容。

什么是Lambda表达式

在Java8中引入了Lambda表达式,这是最令Java开发者激动和期待的一个功能。那究竟什么是Lambda表达式呢?

Lambda表达式本质上是可以传递给其他函数的一小段代码,我们在之前的Java或者Kotlin中,一个函数的参数可以是一种简单的基本数据类型变量或一个对象实例变量。Lambda表达式的出现可以让我们可以把一段代码当做一个值来进行对待,由于是值,便可以进行传递,所以Lambda可以被当做参数传递给其他函数。Lambda表达式的出现,让我们的代码更加简洁。

Kotlin中的Lambda表达式

上面的文字描述可能比较抽象,我们举一个实际的例子。我们定义一个数据类人(对于Kotlin数据类不了解的可以查看这篇文章Kotlin系列之数据类和类委托)。我们的目的是找出一堆人中年龄最大的那个人。

我们先用最基础的Kotlin知识来解决上述问题。代码示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
data class People(val name: String, val age: Int)

fun findTheOldest(peopleList: List<People>) {
var maxAge = 0
var theOldestPeople: People? = null
for (people in peopleList) {
if (people.age > maxAge) {
maxAge = people.age
theOldestPeople = people
}
}
println(theOldestPeople)
}


fun main(args: Array<String>) {
val peopleList = listOf<People>(People("小白", 22),
People("小红", 23), People("琦琦", 20))
findTheOldest(peopleList)
}

但是Kotlin中可以使用库函数配合Lambda将代码进行大大的简化,我们先看看可以最终可以简化到何种程度。代码如下:

1
2
3
4
5
6
fun main(args: Array<String>) {
val peopleList = listOf<People>(People("小白", 22),
People("小红", 23), People("琦琦", 20))
//关键代码就这一行
println(peopleList.maxBy { it.age })
}

是不是被惊艳到了,代码量大大大大减少了,而且代码所表达的功能也更加清楚了。下面我们就来看看上面的那么多代码是怎么利用Lambda表达式变成一行代码的。

Lambda表达式的简化过程

下面就来看看具体的演变过程。
首先maxBy这个函数式类似于一个比较器,它不关心具体的集合中的元素是什么类型,只需要我们传入比较的规则即可,我们上面比较的规则是人们的年龄。所以我们可以把比较规则使用Lambda来书写传入maxBy函数中。

首先先让我们看看Lambda表达式的语法。一个Lambda表达式的基本结构是这样:

1
{参数1: 类型, 参数2: 类型, [...] -> 函数体}

可以看出,一个Lambda表达式被一个花括号包围,参数并没有是哟圆括号括起来,参数列表和函数体之间使用箭头进行分隔。

所以我们依据上面的格式要求,会写出下面这样的代码:

1
peopleList.maxBy({p: People -> p.age})

上面是最原始的Lambda表达式代码。

根据Kotlin的语法约定: 如果Lambda表达式是函数调用的最后一个实参,它可以放在括号的外面。
由于在这个例子中Lambda表达式是maxBy函数的唯一实参,也自然是最后一个实参,所以代码就变成下面这样。

1
peopleList.maxBy(){p: People -> p.age}

但是要记住,当函数有多个实参时,我们既可以将Lambda表达式留在括号内,也可以放在括号的外面,但当我们传递的是两个或更多个Lambda表达式时,不能把超过一个的Lambda表达式放在括号外面。

根据Kotlin的语法约定: 如果Lambda表达式是函数的唯一实参,可以去掉空的圆括号对。
所以上面的代码又会进一步变为下面这样。

1
peopleList.maxBy{p: People -> p.age}

根据Kotlin的语法约定: 如果Lambda表达式中参数的类型可以被推导出来,那么类型声明可以被省略。
这个例子中编译器知道maxBy要处理的每一个元素都是People类型,所以这里不需要显式指定类型。
所以代码被进一步简化为下面这样。

1
peopleList.maxBy{p -> p.age}

当然我们前面也说了,Lambda就是一段代码,我们可以使用变量存储它,便于多次使用。当我们使用变量存储Lambda表达式时,由于缺少上下文环境,所以无法进行参数类型推断,所以必须显式指定参数类型。

根据Kotlin的语法约定: 如果Lambda表达式中只有一个参数,并且这个参数的类型可以被推断出来,那么可以使用默认参数名称it来代替命名参数。
所以最终代码变成了这样。

1
peopleList.maxBy{it.age}

it虽然可缩短代码,但是却不可以滥用,在Lambda表达式嵌套的情况下,it没法清楚表示到底引用的是哪个值,会造成代码的混乱,所以在这种情况下建议显式指定参数名称。

最后再补充一个小点,Lambda表达式并没有限定是单个表达式,他截图包含更多的语句,当包含更多的语句时,最后一个表达式就是Lambda表达式的结果,就像下面这样:

1
2
3
4
5
6
7
fun main(args: Array<String>) {
val sum = {x: Int, y: Int ->
println("start to calc...")
x + y
}
println(sum(1, 2))
}

写在最后

通过上面的介绍,是不是已经被Kotlin中的Lambda表达式吸引了呢,后续还有几篇文章将介绍Kotlin中Lambda表达式的更多内容。

如果博客对您有帮助,不妨请我喝杯咖啡...