语法
一个lambda把一小段行为进行编码,你能把它当作值到处传递。它可以被独立地声明并存储到一个变量中。但是更常用的还是直接声明它并传递给函数。
// 箭头前面的是参数,箭头后面的是函数体
// 始终在花括号内
{ x: Int, y: Int -> x + y }
// 可以把lambda表达式存储在一个变量中,把这个变量当作普通函数对待(即通过相应实参调用它)
val sum = { x: Int, y: Int -> x + y }
println(sum(1, 2))
people.maxBy() { p: People -> p.age }
//当lambda是函数唯一的实参时,还可以去掉调用代码中的空括号对
people.maxBy { p: People -> p.age }
在作用域中访问变量
和Java不一样,kotlin允许在lambda内部访问非fianl
变量甚至修改它们。从lambda内访问外部变量,我们称这些变量被lambda捕捉。
注意,默认情况下,局部变量的声明周期被限制在声明这个变量的函数中。但是如果它被lambda捕捉了,使用这个变量的代码可以被存储并稍后再执行。
你可能会问这是什么原理。当你捕捉final
变量时,它的值和使用这个值的lambda代码一起存储。而非final
变量来说,它的值封装在一个特殊的包装器中,这样你就可以改变这个值,而对这个包装器的引用会和lambda代码一起存储。
fun printProblemCounts(responses: Collection) {
var clientErrors = 0
var serverErrors = 0
responses.forEach {
if (it.startsWith("4")) {
clientErrors++
} else if (it.startsWith("5")) {
serverErrors++
}
}
println("$clientErrors client errors, $serverErrors server errors.")
}
fun main(args: Array) {
val responses = listOf("200 ok", "418 I'm a teapot"
, "500 Internal server error")
printProblemCounts(responses)
}
这里有一个重要的注意事项,如果lambda被用作事件处理器或者用在其他异步执行的情况,对局部变量的修改只会在lambda执行的时候发生。
成员引用
把函数转换成一个值,使用::
运算符来转换。这种表达式称为成员引用,它提供一个调用单个方法或者访问单个属性的函数值。双冒号把类名称与你要应用的成员(一个方法或者一个属性)名称分隔开:
val getAge = Person::age
可以引用顶层函数(不是类成员):
fun salute() = println("salute")
fun main(args: Array) {
run(::salute)
}
集合函数式API
filter
filter
函数遍历集合并选出应用给定lambda后会返回true
的哪些元素:
fun main(args: Array) {
val list = listOf(1,2,3,4)
// 只有偶数留下来
println(list.filter { it % 2 == 0 })
// [2, 4]
}
filter
函数可以从集合中移除你不想要的元素,但是它并不会改变这些元素。
map
map
函数对集合中的每一个元素应用给定的函数并把结果收集到一个新的集合。
fun main(args: Array) {
val list = listOf(1,2,3,4)
println(list.map { it * it })
// [1, 4, 9, 16]
}
map
函数可以变换元素。
all
检查集合中所有元素是否符合某个条件(或者它的变种,是否存在符合的元素)。
fun main(args: Array) {
// 假设存在一个Persion类
val canBeInClub27 = { p: Persion -> p.age <= 27 }
val people = listOf(Persion("a", 27), Persion("b", 31))
// 年龄是否都小于27
println(people.all(canBeInClub27))
// println(people.all { p: Persion -> p.age <= 27 })
// false
}
any
检查集合中任意元素符合某个元素。
fun main(args: Array) {
val list = listOf(1,2,3,4)
println(list.any { it != 3 })
// true
}
count
检查集合中有多少元素满足判断式。
fun main(args: Array) {
val list = listOf(1,2,3,4)
println(list.count{ it != 3 })
// 3
}
find
在集合中找到满足判断式的的元素,但不是所有元素,它按顺序查找。
fun main(args: Array) {
val list = listOf(1,2,3,4)
println(list.find{ it != 3 })
// 1
}
groupBy:把列表转换成分组的map
假设你需要把所有元素按照不同的特征划分成不同的分组,返回结果是Map
类型。例如,你想把人按年龄分组,相同年龄的人放在一组。
fun main(args: Array) {
val list = listOf(Person("a",10),Person("b",10),Person("c",15))
println(list.groupBy { it.age })
// {10=[name=a,age=10, name=b,age=10], 15=[name=c,age=15]}
}
class Person(val name: String, val age: Int) {
override fun toString(): String {
return "name=$name,age=$age"
}
}
flatMap和flatten:处理嵌套集合中的元素
如下:
class Book(val title: String, val authors: List)
// 每本书都可能有一个或者多个作者,可以统计出图书馆中的所有作者的set
// books是集合
books.flatMap { it.authors }.toSet()
flatMap
函数做了两件事情:首先根据作为实参给定的函数对集合中的每个元素做变换(或者说映射),然后把多个列表合并(或者说平铺)成一个列表。
如果你不需要做任何变换,只是需要平铺一个集合,可以使用flatten
函数。