Kotlin提供了不少高级语法特性。不过这些函数对于新手来说比较难于读懂,因为很多情况下,我们总会结合着lambda语法来使用。在Kotlin中的源码标准库(Standard.kt)中提供了一些Kotlin扩展的内置函数可以优化kotlin的编码。
常用高阶函数:
- filter{}:过滤函数。
- takeWhile{}:按照顺序取数,遇到不符合要求的,就停止。
- let{}:调用某对象的let函数,则该对象为函数的参数,在函数块内可以通过it指代该对象。let函数另一个作用就是可以避免写一些判断null的操作。返回值为函数块的最后一行或指定return表达式。
- apply{}:可以直接调用对象的成员变量(方法)。返回值为调用对象自己。apply函数可以和let配合使用,更方便。
- with(){}:以参数的形式传入,并可直接调用对象的成员变量(方法),无需像let函数一样需要使用it来指代对象。
- run{}:run函数实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式。适用于let,with函数任何场景。因为run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理。
- also{}:调用某对象的also函数,则该对象为函数的参数。在函数块内可以通过 it 指代该对象。返回值为该对象自己。also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身。一般可用于多个扩展函数链式调用。
- use{}:通常用于IO操作。即需要关闭资源的操作,使用use函数,能够自动关闭。作用域内可用it指代对象。
it和this的指代:
- let、also、use都会使用it指代当前对象。
- with、run、apply都会使用this指代当前对象或者直接省略。
返回值
- let、with、run均以闭包形式返回。
- apply、also均返回对象自己。
使用场景:
- let:适用于处理不为null的操作场景。
- with:适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上。
- run:适用于let,with函数任何场景。
- also:适用于let函数的任何场景,一般可用于多个扩展函数链式调用
- apply:
- 适用于run函数的任何场景,一般用于初始化一个对象实例的时候,操作对象属性,并最终返回这个对象。
- 动态inflate出一个XML的View的时候需要给View绑定数据也会用到。
- 一般可用于多个扩展函数链式调用。
- 数据model多层级包裹判空处理的问题。
使用示例:
fun main(args: Array<String>) {
// testFilter()
// testLet()
// testApply()
// testApplyAndLet()
// testWith()
// testRun()
// testAlso()
// testReadFile()
// testUse()
}
fun testFilter() {
//求0~6的阶乘
(0..6).map(::factorial).forEach(::println)//map函数是对Array的扩展,是将List<T>转换成 List<R>集合
//只保留阶乘是期奇数的
println((0..6).map(::factorial).filter { it % 2 == 1 })//[1, 1]
//要处于奇数位上的阶乘
println((0..6).map(::factorial).filterIndexed { index, i -> index % 2 == 1 })//[1, 6, 120]
}
fun testTakeWhile() {
//根据条件按照顺序取数,遇到不符合要求的,就停止
println((0..6).map(::factorial).takeWhile { it % 2 == 1 })
}
//阶乘公式 n!=1×2×3×...(n-1)×n
private fun factorial(n: Int): Int {
if (n == 0) return 1//0的阶乘为1
return (1..n).reduce { acc, total -> acc * total }
}
fun testLet() {
val r = findTest()?.let {
it.aa()
println(it.a)
1 + 1
}
println(r)//输出2
}
fun testApply() {
val r = findTest()?.apply {
// 可以使用Test中的成员变量和方法
aa()
println(a)
}
println(r?.b)//apply函数返回调用对象本身(即,Test类的对象),因此可以直接调用其成员变量b
}
fun testApplyAndLet() {
// apply和let一起使用
findTest()?.apply {
aa()
println(a)
findCeShi()?.let {
it.cc()
it.c
//这里的this是指代调用apply函数的对象
println(this.b)
}
}?.bb()//由于apply返回的事对象自己,因此,此处可以直接调用对象的成员方法或变量
}
fun testWith() {
//对象作为with函数的参数
val r = with(findTest()) {
println(this?.a)
this?.aa()
//返回值为函数块的最后一行或指定return表达式。
this?.b
}
println(r)//输出 b参数
}
fun testRun() {
val r = findTest()?.run {
println(a)
aa()
b
}
println(r)//输出 b参数
}
fun testAlso() {
findTest()?.also {
it.a
it.aa()
it.b
}?.bb()//also函数返回调用对象本身(即,Test类的对象),因此可以直接调用其成员变量b
}
//普通读取文件
fun testReadFile() {
val file = File("E:\\blog-note\\test.txt")
//把文件内容读取进缓冲读取器
val bufferedReader = BufferedReader(FileReader(file))
var line: String
while (true) {
//当有内容时读取一行数据,否则退出循环
line = bufferedReader.readLine() ?: break
println(line)
}
bufferedReader.close()//关闭缓冲读取器
}
//使用use函数读取文件,操作结果与上面有一种是一致的。
fun testUse() {
val file = File("E:\\blog-note\\test.txt")
//把文件内容读取进缓冲读取器(use方法会自动对BufferedReader进行关闭)
BufferedReader(FileReader(file)).use {
var line: String
while (true) {
line = it.readLine() ?: break
println(line)
}
}
}
fun findTest(): Test? {
return Test("a参数", "b参数")
}
data class Test(var a: String, var b: String) {
fun aa() {
println("aa is exe!")
}
fun bb() {
println("bb is exe!")
}
}
fun findCeShi(): CeShi? {
return CeShi("c参数", "d参数")
}
data class CeShi(var c: String, var d: String) {
fun cc() {
println("cc is exe!")
}
}