🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 背景 集合的函数式API有很多个,这里我并不打算带你涉猎所有函数式API的用法,而是重点学习函数式API的语法结构,也就是Lambda表达式的语法结构。 首先我们来思考一个需求,如何在一个水果集合里面找到单词最长的那个水果?当然这个需求很简单,也有很多种写法,你可能会很自然地写出如下代码 ~~~ val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon") var maxLengthFruit = "" for (fruit in list) { if (fruit.length > maxLengthFruit.length) { maxLengthFruit = fruit } } println("max length fruit is " + maxLengthFruit)//max length fruit is Watermelon ~~~ 这段代码很简洁,思路也很清晰,可以说是一段相当不错的代码了。但是**如果我们使用集合的函数式API,就可以让这个功能变得更加容易**: ~~~ val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon") val maxLengthFruit = list.maxBy { it.length }//it是单个参数的隐式名称 println("max length fruit is " + maxLengthFruit)//max length fruit is Watermelon ~~~ 上述代码使用的就是函数式API的用法,只用一行代码就能找到集合中单词最长的那个水果。或许你现在理解这段代码还比较吃力,那是因为我们还没有开始学习Lambda表达式的语法结构,等学完之后再来重新看这段代码时,你就会觉得非常简单易懂了。 首先来看一下Lambda的定义,如果用最直白的语言来阐述的话,Lambda就是一小段可以作为参数传递的代码。从定义上看,这个功能就很厉害了,因为正常情况下,我们向某个函数传参时只能传入变量,而借助Lambda却允许传入一小段代码。这里两次使用了“一小段代码”这种描述,**那么到底多少代码才算一小段代码呢?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 } val maxLengthFruit = list.maxBy(lambda) ~~~ 可以看到,**maxBy函数实质上就是接收了一个Lambda参数而已,并且这个Lambda参数是完全按照刚才学习的表达式的语法结构来定义的,因此这段代码应该算是比较好懂的**。 这种写法虽然可以正常工作,但是比较啰嗦,可简化的点也非常多,下面我们就开始对这段代码一步步进行简化。 首先,我们不需要专门定义一个lambda变量,而是可以**直接将lambda表达式传入maxBy函数当中**,因此第一步简化如下所示: ~~~ val maxLengthFruit = list.maxBy({ fruit: String -> fruit.length }) ~~~ 然后**Kotlin规定,当Lambda参数是函数的最后一个参数时,可以将Lambda表达式移到函数括号的外**面,如下所示: ~~~ val maxLengthFruit = list.maxBy() { fruit: String -> fruit.length } ~~~ 接下来,**如果Lambda参数是函数的唯一一个参数的话,还可以将函数的括号省略**: ~~~ val maxLengthFruit = list.maxBy { fruit: String -> fruit.length } ~~~ 这样代码看起来就变得清爽多了吧?但是我们还可以继续进行简化。**由于Kotlin拥有出色的类型推导机制,Lambda表达式中的参数列表其实在大多数情况下不必声明参数类型**,因此代码可以进一步简化成: ~~~ val maxLengthFruit = list.maxBy { fruit -> fruit.length } ~~~ 最后,**当Lambda表达式的参数列表中只有一个参数时,也不必声明参数名,而是可以使用it关键字来代替**,那么代码就变成了: ~~~ val maxLengthFruit = list.maxBy { it.length } ~~~ 怎么样?通过一步步推导的方式,我们就得到了和一开始那段函数式API一模一样的写法,是不是现在理解起来就非常轻松了呢? 正如本小节开头所说的,这里我们重点学习的是函数式API的语法结构,理解了语法结构之后,集合中的各种其他函数式API都是可以快速掌握的。 ### 参考资料 [第一行代码——Android(第3版)](https://www.ituring.com.cn/book/tupubarticle/30209)