博客主页java
// lambda表达式显现监听器 button.setOnClickListener { /* ... */ }
data class Person( val name: String, val age: Int ) >>> val list = listOf(Person("kerwin", 12), Person("Bob", 23)) // 用lambda在集合中搜索,比较年龄找到最大的元素 >>> // fun <T, R : Comparable<R>> Iterable<T>.maxBy(selector: (T) -> R): T? // 接收一个集合中的元素做为实参(使用it引用它)并返回用来比较的值,简明写法 // 若是只有一个参数的lambda,且这个参数的类型能够推导出来,会生成默认参数名称it >>> println(list.maxBy { it.age }) // 花括号中的代码片断是lambda表达式,把它做为实参传给这个函数 // 这个lambda接收一个类型为Person的参数并返回它的年龄 >>> list.maxBy { person -> person.age } // 若是lambda恰好是函数或者属性的委托,能够用成员引用替换 >>> println(list.maxBy(Person::age))
一个lambda把一段行为进行编码,能把它看成值处处传递,能够被独立的声明并存储到一个变量中。编程
// lambda表达式的语法 // 参数 -> 函数体 { x: Int, y: Int -> x + y } // kotlin 的lambda表达式始终用花括号({})包围。 // 实参并无用括号括起来,箭头把实参列表和lambda的函数体隔开
能够把lambda表达式存储在一个变量中,把这个变量看成普通函数对待segmentfault
// 使用变量存储lambda,不能推导出参数类型,必须显式的指定参数类型 val sum = { x: Int, y: Int -> x + y } println(sum(1, 2)) // 调用保存在变量中的lambda
kotlin中若是lambda表达式是函数调用的最后一个实参,它能够放到括号的外边app
list.maxBy() { person: Person -> person.age } // 当lambda是函数惟一的实参时,能够去掉调用代码中的空括号 // 显式的写出参数类型 list.maxBy { person: Person -> person.age } // 能够不写参数类型,会根据上下文推导出参数类型 list.maxBy { person -> person.age }
使用命名实参来传递lambdaide
val list = listOf(Person("kerwin", 12), Person("Bob", 23)) // 把lambda做为命名实参传递 val names = list.joinToString(separator = " ", transform = { person: Person -> person.name }) println(names) // kerwin Bob // 能够把lambda放在括号外传递 val names = list.joinToString(separator = " ") { person: Person -> person.name }
在函数内部使用lambda,也能够访问这个函数的参数,还能够在lambda以前定义局部变量函数
// 在lambda中使用函数参数 fun printMessageWithPrefix(messages: Collection<String>, prefix: String) { messages.forEach { // lambda中访问prefix参数 println("$prefix $it") } } val messages = listOf("404", "403") printMessageWithPrefix(messages, "error: ")
kotlin容许lambda内部访问非final变量甚至修改它们,从lambda内访问外部变量,称这些变量被lambda捕捉。post
// 成员引用语法,使用::运算符 // 类::成员 Person::age
:: 运算符 能够把函数转换成一个值,如:val getAge = Person::age ,这种表达式称为成员引用ui
还能够引用顶层函数(不是类的成员)this
fun test() = println("test") // 引用顶层函数,省略了类的名称,直接 ::开头 // 成员引用 ::test被看成实参传递给库函数,它会调用相应的函数 run(::test)
若是lambda要委托给一个接收多个参数的函数,成员引用代替会很是方便编码
// 这个lambda委托给sendEmail函数 val action = { person: Person, message: String -> sendEmail(person, message) } // 可使用成员引用代替 val nextAction= ::sendEmail >>> action(Person("kerwin",12), "吃饭") >>> nextAction(Person("bob",34), "吃饭")
能够用 构造方法引用 存储或者延期执行建立类实例的动做。构造方法引用的形式是在双冒号(::)后指定类名称
data class Person( val name: String, val age: Int ) // 构造方法引用,建立Person实例的动做被保存成了值 val createPerson = ::Person val person = createPerson("kerwin", 23) println(person)
还能够引用扩展函数
// Person类扩展函数 fun Person.isAdult() = this.age >= 21 val person = Person("kerwin", 22) // 虽然isAdult不是Person类的成员,但还能够经过引用访问它 val predicate = Person::isAdult println(predicate(person))
filter 函数底层源码:
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> { return filterTo(ArrayList<T>(), predicate) } public inline fun <T, C : MutableCollection<in T>> Iterable<T>.filterTo(destination: C, predicate: (T) -> Boolean): C { for (element in this) if (predicate(element)) destination.add(element) return destination }
从filter 函数源码可知:filter函数遍历集合并选出知足给定lambda后返回true的元素,这些知足条件的元素存放在新的集合中。
val list = listOf(1, 2, 3, 4) // 过滤出偶数元素 println(list.filter { it % 2 == 0 }) // [2, 4] val list = listOf(Person("kerwin", 23), Person("Bob", 12)) // 过滤出年龄超过18的人 println(list.filter { it.age >= 18 }) // [Person(name=kerwin, age=23)]
map 函数底层源码:
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> { return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform) } public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.mapTo(destination: C, transform: (T) -> R): C { for (item in this) destination.add(transform(item)) return destination }
从map 函数源码可知:map函数对集合中的每个元素应用给定的lambda后并把结果存储在一个新的集合中。新集合中元素个数不变,但每一个元素根据给定的lambda作了变换。
val list = listOf(1, 2, 3, 4) println(list.map { it * it }) // [1, 4, 9, 16] val list = listOf(Person("kerwin", 23), Person("Bob", 12)) // 只须要姓名列表,可使用map转换 println(list.map { it.name }) // kerwin, Bob] // 可使用成员引用 list.map(Person::name) // filter 和 map 能够组合使用 // 输出年龄超过18岁的姓名 println(list.filter { it.age >= 18 }.map { it.name }) // [kerwin] // 找出年龄最大的人思路:先在集合中找到最大年龄,而后过滤最大年龄的人 val maxAge = list.maxBy(Person::age)?.age println(list.filter { it.age == maxAge })
还能够对Map集合应用过滤和变换函数:
val map = mapOf(1 to "one", 2 to "two") println(map.mapValues { it.value.toUpperCase() }) // {1=ONE, 2=TWO} // 键和值分别由各自的函数来处理。 // filterKeys 和 mapKeys 过滤和变换 Map的键 // filterValues 和 mapValues 过滤和变换 Map的值
all 和 any 函数检查集合中的全部元素是否都符合某个条件(或者它的变种,是否存在符合的元素);
count 函数检查有多少元素知足判断式;
find 函数返回第一个符合条件的元素。
all函数底层源码:
/** * Returns `true` if all elements match the given [predicate]. */ public inline fun <T> Iterable<T>.all(predicate: (T) -> Boolean): Boolean { if (this is Collection && isEmpty()) return true for (element in this) if (!predicate(element)) return false return true }
从 all 函数源码可知:集合中的全部元素都知足条件才返回true,不然返回false。
val list = listOf(Person("kerwin", 12), Person("bob", 19)) val result = list.all { person: Person -> person.age >= 18 } println(result) false
any 函数底层源码:
public inline fun <T> Iterable<T>.any(predicate: (T) -> Boolean): Boolean { if (this is Collection && isEmpty()) return false for (element in this) if (predicate(element)) return true return false }
从 any 函数可知:集合中只要有一个元素知足条件就返回true,不然返回false
val list = listOf(Person("kerwin", 12), Person("bob", 19)) val result = list.any { person: Person -> person.age >= 18 } println(result) true
count 函数底层源码:
public inline fun <T> Iterable<T>.count(predicate: (T) -> Boolean): Int { if (this is Collection && isEmpty()) return 0 var count = 0 for (element in this) if (predicate(element)) checkCountOverflow(++count) return count }
从 count 函数可知:集合中元素知足条件的个数
val result = list.count { person: Person -> person.age >= 10 } println(result) 2
find 函数底层源码:
public inline fun <T> Iterable<T>.find(predicate: (T) -> Boolean): T? { return firstOrNull(predicate) } public inline fun <T> Iterable<T>.firstOrNull(predicate: (T) -> Boolean): T? { for (element in this) if (predicate(element)) return element return null }
从 find 函数可知:查找集合中第一个知足条件的元素,找到返回该元素,不然返回null
val result = list.find { person: Person -> person.age >= 18 } println(result) Person(name=bob, age=19)
groupBy 函数底层源码:
public inline fun <T, K> Iterable<T>.groupBy(keySelector: (T) -> K): Map<K, List<T>> { return groupByTo(LinkedHashMap<K, MutableList<T>>(), keySelector) } public inline fun <T, K, M : MutableMap<in K, MutableList<T>>> Iterable<T>.groupByTo(destination: M, keySelector: (T) -> K): M { for (element in this) { val key = keySelector(element) val list = destination.getOrPut(key) { ArrayList<T>() } list.add(element) } return destination } public inline fun <K, V> MutableMap<K, V>.getOrPut(key: K, defaultValue: () -> V): V { val value = get(key) return if (value == null) { val answer = defaultValue() put(key, answer) answer } else { value } }
从 groupBy 函数可知:把集合中全部的元素按照不一样的特性分红不一样的分组,返回Map集合,key:分组条件。value:List集合,每一个分组都存储在一个列表中。
val list = listOf( Person("kerwin", 12), Person("bob", 19), Person("Alice", 12) ) // 按照年龄分组,把相同年龄的人放在一组 val groupList = list.groupBy { person: Person -> person.age } println(groupList) {12=[Person(name=kerwin, age=12), Person(name=Alice, age=12)], 19=[Person(name=bob, age=19)]}
flatMap 函数底层源码:
public inline fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R> { return flatMapTo(ArrayList<R>(), transform) } public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.flatMapTo(destination: C, transform: (T) -> Iterable<R>): C { for (element in this) { val list = transform(element) destination.addAll(list) } return destination }
从 flatMap 函数可知:根据lambda给定的表达式(要返回的是Iterable子类)对每个元素作变换(或者说映射),而后把多个列表合并(或者说平铺)成一个列表。
val books = listOf( Book("java", listOf("abc", "bcd")), Book("kotlin", listOf("wer")) ) // 统计集合中每本书的做者合并一个扁平的列表 val bookAllAuthors = books.flatMap { book: Book -> book.authors } println(bookAllAuthors) [abc, bcd, wer]
不少链式集合函数调用的时候,如:map 和 filter,这些函会及早的建立中间集合,也就是说每一步的中间结果都被存储在一个临时列表。序列 能够避免建立这些临时中间对象。
// 这种方式会建立临时中间对象 // 特色:先在每一个元素上调用map函数,而后在结果列表中的每一个元素上再调用filter函数 list.map { println("map: $it") it.name } .filter { println("filter: $it") it.startsWith("k") } .toList() // 为了提升效率,能够把操做先变成序列,而不是直接使用集合 // 特色:全部操做是按照顺序在每个元素上,处理完第一个元素(先映射再过滤),而后完成第二个元素的处理,以此类推 list.asSequence() // 把初始集合转换成序列 .map { println("map: $it") it.name } // 序列支持和集合同样的API .filter { println("filter: $it") it.startsWith("k") } .toList() // 把结果序列转换回列表,反向转换
kotlin惰性集合操做的入口就是:Sequence 接口,这个接口表示的就是一个能够列举元素的元素序列。它只提供了一个 方法iterator,用来从序列中获取值。序列中的元素求值是惰性的,因此使用序列能够更高效的对集合元素执行链式操做。
public interface Sequence<out T> { /** * Returns an [Iterator] that returns the values from the sequence. * * Throws an exception if the sequence is constrained to be iterated once and `iterator` is invoked the second time. */ public operator fun iterator(): Iterator<T> }
序列操做分为两类:中间的和末端的。一次 中间操做 返回的是另外一个序列,一个新序列知道如何变换原始序列中的元素;一次 末端操做 返回的是一个结果,这个结果多是集合、元素、数字或者从初始集合的变换序列中获取的任意对象。
list.asSequence() .map(Person::name).filter { it.startsWith("k") } // 中间操做,始终是惰性的 .toList() // 末端操做
除了在集合上调用asSequence() 建立序列,还可使用generateSequence函数。
// 计算100之内全部天然数之和 val naturalNumbers = generateSequence(0) { it + 1 } val numbersTo100 = naturalNumbers.takeWhile { it <= 100 } // 当获取结果sum时,全部被推迟的操做都被执行 println(numbersTo100.sum()) // 5050
takeWhile 函数在集合中底层源码:
public inline fun <T> Iterable<T>.takeWhile(predicate: (T) -> Boolean): List<T> { val list = ArrayList<T>() for (item in this) { if (!predicate(item)) break list.add(item) } return list }
kotlin的lambda能够无缝地和java API互操做。
能够把lambda传给任何指望函数式接口的方法。
//java void postponeComputation(int delay, Runnable computation) //kotlin中,能够把lambda做为实参传给它,编译器会自动转换成一个Runnable 实例 // "一个Runnable 实例":指的是一个实现了Runnable 接口的匿名类的实例 // 整个程序只会建立一个Runnable实例 postponeComputation(1000) { println("kotlin") } // 也能够把对象表达式做为函数式接口的实现传递 // 这种方式每次调用都会建立一个新的实例 postponeComputation(1000,object : Runnable { override fun run() { } }) // 编程成全局的变量,程序中仅此一个实例,每次调用时都是同一个对象 val runnable = Runnable { println("kotlin") } fun handleComputation() { postponeComputation(1000, runnable) }
lambda从包围它的做用域中捕捉了变量,每次调用就再也不可能重用同一个实例了
fun handleComputation(id: String) { // lambda会捕捉id这个变量 // 每次handleComputation调用时都会建立一个Runnable新实例 postponeComputation(1000) { println(id) } }
SAM构造方法 是编译器生成的函数,让你执行从lambda到函数式接口实例的显式转换。
带单抽象方法的接口,叫做SAM接口
若是有一个方法返回的是一个函数式接口的实例,不能直接返回一个lambda,要用SAM构造方法把它包装起来
// 使用SAM构造方法返回值 // SAM构造方法的名称和底层函数式接口名称同样 // SAM构造方法只接收一个参数(一个被用做函数式接口单抽象方法体的lambda),并返回实现了这个接口的类的一个实例 fun callAllDoneRunnable() : Runnable { return Runnable { println("All Done.") } } callAllDoneRunnable().run()
SAM构造方法还能够用在须要把从lambda生成的函数式接口实例存储在一个变量中
// 使用SAM构造方法来重用listener实例 val listener = OnClickListener { view -> // 使用view.id来判断点击的是哪个按钮 val text = when(view.id) { R.id.button1 -> "First Button" R.id.button1 -> "Second Button" else -> "Unknown Button" } toast(text) } button1.setOnClickListener(listener) button2.setOnClickListener(listener)
在lambda函数体内能够调用一个不一样对象的方法,并且无须借助任何额外限定符,这样的lambda叫做带接收者的lambda
with 函数:能够用它对同一个对象执行屡次操做,而不须要反复把对象的名称写出来。
// 这段代码调用了result 实例上好几个不一样的方法,且每次调用都重复result这个名称 fun alphabet(): String { val result = StringBuilder() for (letter in 'A'..'Z') { result.append(letter) } result.append("\nNow I known the alphabet") return result.toString() }
使用 with 函数 重写上段代码,with 函数接收两个参数。
// fun <T, R> with(receiver: T, block: T.() -> R): R // 使用with构造 fun alphabet(): String { val stringBuilder = StringBuilder() // 指定接受者的值 return with(stringBuilder) { for (letter in 'A'..'Z') { // 经过显式的this来调用接收者值的方法 this.append(letter) } // 省略this能够调用方法 append("\nNow I known the alphabet") // 从lambda返回值 this.toString() } }
使用with和一个表达式函数体来构建字母表
// 使用表达式函数体语法 fun alphabet() = with(StringBuilder()) { for (letter in 'A'..'Z') { append(letter) } append("\nNow I known the alphabet") toString() }
apply 函数始终会返回做为实参传递给它的对象(换句话说:接收者对象)
// 它的接收者变成了做为实参的lambda接收者,执行apply结果是StringBuilder fun alphabet() = StringBuilder().apply { for (letter in 'A'..'Z') { append(letter) } append("\nNow I known the alphabet") }.toString()
在java中,一般是经过另一个单独的Builder对象来完成的;在kotlin中,能够在任意对象上使用apply函数,彻底不须要自定义对象来完成。
// 使用apply初始化一个TextView fun createViewWithCustomAttributes(context: Context) = TextView(context).apply { text = "Sample Text" textSize = 20.0 setPading(10, 0, 0, 0) }
可使用kotlin标准库函数buildString,它会负责建立StringBuilder并调用toString
fun alphabet() = buildString { for (letter in 'A'..'Z') { append(letter) } append("\nNow I known the alphabet") }
若是个人文章对您有帮助,不妨点个赞鼓励一下(^_^)