語法
一個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<String>) {
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<String>) {
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<String>) {
run(::salute)
}
集合函數式API
filter
filter
函數遍歷集合并選出應用給定lambda后會返回true
的哪些元素:
fun main(args: Array<String>) {
val list = listOf(1,2,3,4)
// 只有偶數留下來
println(list.filter { it % 2 == 0 })
// [2, 4]
}
filter
函數可以從集合中移除你不想要的元素,但是它并不會改變這些元素。
map
map
函數對集合中的每一個元素應用給定的函數并把結果收集到一個新的集合。
fun main(args: Array<String>) {
val list = listOf(1,2,3,4)
println(list.map { it * it })
// [1, 4, 9, 16]
}
map
函數可以變換元素。
all
檢查集合中所有元素是否符合某個條件(或者它的變種,是否存在符合的元素)。
fun main(args: Array<String>) {
// 假設存在一個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<String>) {
val list = listOf(1,2,3,4)
println(list.any { it != 3 })
// true
}
count
檢查集合中有多少元素滿足判斷式。
fun main(args: Array<String>) {
val list = listOf(1,2,3,4)
println(list.count{ it != 3 })
// 3
}
find
在集合中找到滿足判斷式的的元素,但不是所有元素,它按順序查找。
fun main(args: Array<String>) {
val list = listOf(1,2,3,4)
println(list.find{ it != 3 })
// 1
}
groupBy:把列表轉換成分組的map
假設你需要把所有元素按照不同的特征劃分成不同的分組,返回結果是Map<K, List<T>>
類型。例如,你想把人按年齡分組,相同年齡的人放在一組。
fun main(args: Array<String>) {
val list = listOf<Person>(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<String>)
// 每本書都可能有一個或者多個作者,可以統計出圖書館中的所有作者的set
// books是集合
books.flatMap { it.authors }.toSet()
flatMap
函數做了兩件事情:首先根據作為實參給定的函數對集合中的每個元素做變換(或者說映射),然后把多個列表合并(或者說平鋪)成一個列表。
如果你不需要做任何變換,只是需要平鋪一個集合,可以使用flatten
函數。