Java从JDK1.8之后开始加入了Lambda编程的语法支持.而Kotlin从第一个版本就开始支持.
本文将通过集合的函数式API来学习Lambda编程.
现在我们有一个需求,创建一个包含许多水果名称的集合.
如果是在Java中,你会怎么做呢?可能你首先会创建一个ArrayList的实例,然后将说过的名称一个个添加到集合中.当然,在Kotlin中也可以这么做.
val list = ArrayList<String>()
list.add("Apple")
list.add("Banana")
list.add("Orange")
list.add("Pear")
list.add("Grape")
list.add("Watermelon")
在Kotlin中,有一种更简洁的表达方式,如下:
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
现在有一个新的需求,找出上述水果集合中,单词最长的那个水果?这个需求也比较简单.
代码如下:
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
var maxLengthFruit = ""
for (fruit in list) {
if (fruit.length > maxLengthFruit.length) {
maxLengthFruit = fruit
}
}
Log.d("最长单词的水果是:", maxLengthFruit)
如果我们使用Koltin中集合的函数式API,这个功能可以更加简单:
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val maxLengthFruit = list.maxBy {
it.length }
Log.d("最长单词的水果是:", maxLengthFruit)
上述代码使用的就是函数式API的用法,只用一行代码就能找到集合中单词最长的那个水果.
现在我们通过这一行代码,来学习Kotlin表达式的语法结构。
首先来看一下Lambda的定义,如果用最直白的语言来阐述,Lambda就是一小段可以作为参数传递的代码。从定义上看,这个功能就很厉害了,因为正常情况下,我们向某个函数传参数时只能传入变量,而借助Lamada却运行传入一小段代码。至于多少才算“一小段代码”,Kotlin没有进行限制。但是通常不建议在Lambda表达中编写太长的代码,否则可能会影响代码的可读性。
接着我们来看一下Lambda表达式的语法结构:
{ 参数名1:参数类型,参数名2:参数类型 ->函数体 }
这是Lambda表达式最完整的语法结构定义.首先最外层是一对大括号,如果有参数传入到Lambda表达式中的话,我们还需要声明参数列表, 参数列表的结尾使用一个 -> 符合,表示参数列表中的结束以及函数体的开始,函数体中可以编写任意行代码,并且最后一行代码会自动作为Lambda表达式的返回值.
当然,在很多情况下,我们并不需要使用Lambda表达式完整的语法结构,而是又很多种简化的写法.但是简化版的写法对于初学者而言更难理解,因此这里我准备使用一步步推到演化的方式,向你展示这些简化版的写法是从何而来的,这样你就能对Lambda表达式的语法结构理解得更加彻底了.
回到刚刚的找到最长单词的需求,前面使用的函数式API的语法结构看起来好像很特殊,但其实maxBy就是一个普通的函数而已,只不过它接收的是一个Lambda类型的参数,并且会在遍历集合时将每次遍历的值作为参数传递给Lambda表达式.maxBy函数的工作原理是根据我们传入的条件来遍历集合,从而找到该条件下的最大值,比如我们说想要找到单词最长的水果,那么条件自然就是单词的长度了.
理解了maxBy函数的工作原理之后,我们就可以套用刚才学习的Lambda表达式的语法结构,并将它传入到maxBy函数中,如下所示:
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val lambda = {
fruit: String -> fruit.length }
Log.d("最长单词的水果是:", list.maxBy(lambda))
可以看到,maxBy函数实质上就是收到了一个Lambda参数而已,并且这个Lambda参数是完全按照刚才学的表达式的语法结构来定义的,因此这段代码应该算是比较好懂的.
这种写法虽然可以正常工作,但是比较啰嗦,可简化的点也比较多,下面我们就开始对这段代码进行一步步简化.
首先,我们不需要专门定义一个Lamada变量,而是可以直接将lambda表达式移到maxBy函数当中,因此第一步简化如下所示:
val maxLenghtFruit = list.maxBy({
fruit: String -> fruit.length })
然后Kotlin规定,当Lambda参数是函数的最后一个参数时,可以将Lambda表达式移到函数括号的外面,如下所示:
val maxLenghtFruit = list.maxBy() {
fruit: String -> fruit.length }
接下来,如果Lambda参数时函数的唯一一个参数时,还可以将函数的括号省略:
val maxLenghtFruit = list.maxBy {
fruit: String -> fruit.length }
这样代码看上去就变得清爽了很多.但是我们还可以继续简化.由于Kotlin拥有出色的类型推导机制,Lambda表达式中的参数列表其实在大多数情况下不必声明参数类型,因此代码可以进一步简化成:
val maxLenghtFruit = list.maxBy {
fruit -> fruit.length }
最后,当Lambda表达式的参数列表中只有一个参数时,也不必声明参数名,而是可以使用it关键字来替代,那么代码就变成了:
val maxLenghtFruit = list.maxBy {
it.length }
通过一步步的推导,我们就得到了与 最开始那段函数式API 一模一样的写法.
以上,就是Lambda表达式语法糖的内容.