Kotlin 一个强大之处就在于它的扩展函数,巧妙的运用这些扩展函数可以让你写出的代码更加优雅,阅读起来更加流畅。
内联函数
在写代码的时候难免会遇到这种情况,就是很多处的代码是一样的,于是通常会抽取出一个公共方法来进行调用,这样看起来就会很简洁;但是也出现了一个问题,就是这个方法会被频繁调用,就会很耗费资源。
举个例子:
fun <T> method(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
} finally {
lock.unlock()
}
}
这里的 method() 方法在调用的时候是不会把形参传递给其他方法的,调用一下:
method(lock, { "我是body的方法体" })// lock是一个Lock对象
对于编译器来说,调用 method() 方法就要将参数 lock 和 lambda 表达式 {"我是body的方法体"} 进行传递,就要将 method() 方法进行压栈出栈处理,这个过程就会耗费资源。如果是很多地方调用,就会执行很多次,这样就非常消耗资源了,于是就引入了内联函数。
被 inline 关键字标记的函数就是内联函数,其原理就是:在编译时期,把调用这个函数的地方用这个函数的方法体进行替换。
标准函数
在 Kotlin 源码的 Standard.kt 文件中提供了一些很好用的内置高阶函数,可以帮助我们写出更优雅的 Kotlin 代码,提高生产力。
let()
定义
let() 函数的定义如下:
public inline fun <T, R> T.let(block: (T) -> R): R
功能
let() 函数的功能:定义一个变量在一个特定的作用域范围内使用,返回值为函数的最后一行或 return 表达式。
with()
定义
with() 函数的定义如下:
public inline fun <T, R> with(receiver: T, block: T.() -> R): R
功能
with() 函数的功能:使用给定的 receiver 作为接收器调用指定的函数 block,在函数内可以通过 this 指代该对象,返回值为函数的最后一行或 return 表达式。
示例
在自定义 View 中初始化画笔时很多时候会写下以下代码:
var paint = Paint() paint.color = Color.BLACK paint.strokeWidth = 1.0f paint.textSize = 18.0f paint.isAntiAlias = true如果使用
with()函数,那么就可以写成这样var paint = Paint() with(paint) { color = Color.BLACK strokeWidth = 1.0f textSize = 18.0f isAntiAlias = true }省去了
paint.,后书写起来感觉会更加自然。在声明一些集合的场景,比如:
var list = mutableListOf<String>() list.add("1") list.add("2") list.add("3")如果使用
with()函数,那么就可以写成这样var list = with(mutableListOf<String>()) { add("1") add("2") add("3") this }
run()
定义
run() 函数的定义如下:
public inline fun <R> run(block: () -> R): R
public inline fun <T, R> T.run(block: T.() -> R): R
功能
run() 函数的功能:调用指定的函数 block,以闭包形式返回,返回值为函数的最后一行或 return 表达式。
示例
返回 return 表达式,return 后面的代码不再执行(注意写法 @run):
run {
return@run println("11")
println("22")
}
apply()
定义
apply() 函数的定义如下:
public inline fun <T> T.apply(block: T.() -> Unit): T
功能
apply() 函数的功能:使用 this 值作为接收器调用指定的函数 block,在函数范围内可以任意调用该对象的任意方法,返回值为 this 值(传入对象的本身)。
示例
在自定义 View 中初始化画笔时很多时候会写下以下代码:
var paint = Paint() paint.color = Color.BLACK paint.strokeWidth = 1.0f paint.textSize = 18.0f paint.isAntiAlias = true如果使用
apply()函数,那么就可以写成这样var paint = Paint().apply { color = Color.BLACK strokeWidth = 1.0f textSize = 18.0f isAntiAlias = true }在声明一些集合的场景,比如:
var list = mutableListOf<String>() list.add("1") list.add("2") list.add("3")如果使用
apply()函数,那么就可以写成这样var list = mutableListOf<Int>().apply { add(1) add(2) add(3) }
注意
apply()和run()函数的区别,run()函数返回的是最后一行,apply()函数返回的是对象本身。由apply()函数的定义可以看出apply()函数适用于那些对象初始化需要给其属性赋值的情况。
also()
定义
also() 函数的定义如下:
public inline fun <T> T.also(block: (T) -> Unit): T
功能
also() 函数的功能:使用 this 值作为参数调用指定的函数 block,在函数范围内可以通过 it 指代该对象,返回值为 this 值(传入对象的本身)。
示例
在自定义 View 中初始化画笔时很多时候会写下以下代码:
var paint = Paint()
paint.color = Color.BLACK
paint.strokeWidth = 1.0f
paint.textSize = 18.0f
paint.isAntiAlias = true
如果使用 apply() 函数,那么就可以写成这样
var textView = Paint().also {
it.color = Color.BLACK
it.strokeWidth = 1.0f
it.textSize = 18.0f
it.isAntiAlias = true
}
功能
let() 函数的功能:使用 this 值作为参数调用指定的函数 block,在函数范围内可以通过 it 指代该对象,返回值为函数的最后一行或 return 表达式。
示例
let() 函数有点类似于 run() 函数,let() 函数在使用中可用于空安全验证,变量?.let{}:
var paint: Paint?
paint?.let {
it.color = Color.BLACK
it.strokeWidth = 1.0f
it.textSize = 18.0f
it.isAntiAlias = true
}
takeIf()
定义
takeIf() 函数的定义如下:
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T?
功能
takeIf() 函数的功能:使用 this 值作为参数判断是否满足给定的 predicate,如果满足则返回 this,否则返回 null。
takeUnless()
定义
takeUnless() 函数的定义如下:
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T?
功能
takeUnless() 函数的功能:使用 this 值作为参数判断是否满足给定的 predicate,如果不满足则返回 this,否则返回 null,和 takeIf() 函数正好相反。
repeat()
定义
repeat() 函数的定义如下:
public inline fun repeat(times: Int, action: (Int) -> Unit)
功能
repeat() 函数的功能:连续执行指定次数的指定函数 action,其实就是简化了 for 循环。
函数选择

函数区别
| 函数名 | 定义inline的结构 | 函数体内使用的对象 | 返回值 | 是否是扩展函数 | 适用的场景 |
|---|---|---|---|---|---|
let |
fun <T, R> T.let(block: (T) -> R): R = block(this) | it指代当前对象 | 闭包形式返回 | 是 | 适用于处理不为null的操作场景 |
with |
fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block() | this指代当前对象或者省略 | 闭包形式返回 | 否 | 适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上 |
run |
fun <T, R> T.run(block: T.() -> R): R = block() | this指代当前对象或者省略 | 闭包形式返回 | 是 | 适用于let,with函数任何场景 |
apply |
fun T.apply(block: T.() -> Unit): T { block(); return this } | this指代当前对象或者省略 | 返回this | 是 | 适用于run函数的任何场景,一般用于初始化一个对象实例的时候,操作对象属性,并最终返回这个对象;动态inflate出一个XML的View的时候需要给View绑定数据也会用到;一般可用于多个扩展函数链式调用;数据model多层级包裹判空处理的问题 |
also |
fun T.also(block: (T) -> Unit): T { block(this); return this } | it指代当前对象 | 返回this | 是 | 适用于let函数的任何场景,一般可用于多个扩展函数链式调用 |