持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第一天,点击查看活动详情
Kotlin深入浅出系列---let、run、apply、with、also
前言
在我们学习Kotlin语法中,会看到以下五种函数:let、run、with、apply和also。这五种函数在Kotlin中叫做作用域函数,是Kotlin标准库中的函数,它们的唯一目的是在对象的上下文中执行代码块。
- 作用域函数 :当一个对象调用这个样的函数并提供一个lambda表达式时,他会形成一个临时作用域。在此作用域中,可以访问该对象而无需其名称
简单来说:可以减少冗余代码,从而让代码更简洁;可以让代码形成链式调用,从而使代码逻辑更清晰。
作用域函数
我们在上面已经了解到了let、run、with、apply和also都是在在做一件事情,在对象的上下文中执行代码块
使用作用函数
val paint = Paint()
paint.let {
//设置画笔颜色
it.color = Color.RED
//设置填充样式--填充
it.style =Paint.Style.FILL
//设置画笔宽度
it.strokeWidth =50F
}
不使用作用函数
val paint = Paint()
//设置画笔颜色
paint.color = Color.RED
//设置填充样式--填充
paint.style =Paint.Style.FILL
//设置画笔宽度
paint.strokeWidth =50F
通过上面的代码,我们看出在不引用任何新技术的情况下,我们使用作用函数使代码更加的简洁、易读
let、run、with、apply和also具体使用与区别
我们知道let、run、with、apply和also都是作用函数,我们来看一下它们的具体使用和区别
let
调用某对象的let函数,则该对象为函数的参数。在函数块内可以通过 it 指代该对象。返回值为函数块的最后一行或指定return表达式
源码:
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
使用:
val paint = Paint()
val letReturn = paint.let {
it.color = Color.RED
//设置填充样式--填充
it.style =Paint.Style.FILL
//设置画笔宽度
it.strokeWidth =50F
}
let会返回最后一行代码,这里返回的是Unit,所以letReturn是Unit
val letReturn = paint.let {
it.color = Color.RED
//设置填充样式--填充
it.style =Paint.Style.FILL
//设置画笔宽度
it.strokeWidth =50F
1
}
这里letReturn就是1啦
run
调用某对象的run函数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式
源码:
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
使用:
val paint = Paint()
val runReturn = paint.run {
this.color = Color.RED
//设置填充样式--填充
this.style =Paint.Style.FILL
//设置画笔宽度
this.strokeWidth =50F
}
这里it变成了this,返回值也会是this(调用run函数自身,这里就是paint)
runReturn就是paint
with
with函数和前面的几个函数使用方式略有不同,因为它不是以扩展的形式存在的。它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值和let一样,为函数块的最后一行或指定return
源码:
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
使用:
val paint = Paint()
val withReturn = with(paint) {
this.color = Color.RED
//设置填充样式--填充
this.style =Paint.Style.FILL
//设置画笔宽度
this.strokeWidth =50F
}
这里和let返回值是一样的,不做过多解释,详细请看let
apply
用某对象的apply函数,在函数块内可以通过 this 指代该对象。返回值为该对象自己
源码:
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
使用:
val paint = Paint()
val applyReturn = paint.apply {
this.color = Color.RED
//设置填充样式--填充
this.style =Paint.Style.FILL
//设置画笔宽度
this.strokeWidth =50F
}
返回值是调用者本身,所以applyReturn为paint
also
调用某对象的also函数,则该对象为函数的参数。在函数块内可以通过 it 指代该对象。返回值为该对象自己。
源码
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
使用:
val paint = Paint()
val alsoReturn = paint.also {
it.color = Color.RED
//设置填充样式--填充
it.style =Paint.Style.FILL
//设置画笔宽度
it.strokeWidth =50F
}
返回值为调用者本身,所以alsoReturn为paint
总结
| 函数 | 函数块内使用对象 | 返回值 | 是否为扩展函数 | 使用场景 |
|---|---|---|---|---|
| let | it | 函数块最后一行或return表达式的值 | 是 | 适用于对象统一处理不为空的情况 |
| run | this | 函数块最后一行或return表达式的值 | 是 | 适用with()、let()函数的任何场景 |
| apply | this | 该对象本身 | 是 | 适用于run()函数的任何场景,通常可用来在初始化一个对象实例时,操作对象属性并最终返回该对象。也可用于多个扩展函数链式调用 |
| with | this | 函数块最后一行或return表达式的值 | 否,对象作为参数 | 适用于调用同一个类多个方法 |
| also | it | 该对象本身 | 是 | 适用于let()函数的任何场景,一般可用于多个扩展函数链式调用 |
上下文对象:this还是it
我们在上文中了解到,我们的作用域函数中,可以使用it或者this来访问上下文对象。
- lambda 表达式的参数(it) : let、also
- lambda表达式的接收者(this): run、with、apply
lambda表达式的接收者 this
run、with和apply通过this关键字来进行对上下文的访问,因此,在其lambda表达式中我们可以在类函数中一样访问上下文。当然我们也可以省略this,当我们要对外部对象的成员进行修改时,我们可以使用This 表达式来表达该成员变量为外部类的
val paint = Paint()
paint.apply {
this.color = Color.RED
//设置填充样式--填充
this.style =Paint.Style.FILL
//设置画笔宽度
this.strokeWidth =50F
也可以省略this、效果是一样的
color = Color.RED
style =Paint.Style.FILL
strokeWidth =50F
}
//
class Test :View(){
var color = Color.RED
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val paint = Paint()
paint.apply {
//使用this语法来调用外部类的成员变量
this@Test.color = Color.RED
}
}
lambda 表达式的参数 it
it和also是将上下文对象作为lambda表达式参数来进行对上下文的访问。如果没有指定参数名,默认为it,it比this更易读,因此,当上下文对象在作用域中,主要用作对函数的调用中的参数时,使用it作为上下文对象会更好
val paint = Paint()
paint.let { //这里不写参数名默认为it 也可以指定,如:value ->来进行修改默认参数名
//设置画笔颜色
it.color = Color.RED
//设置填充样式--填充
it.style =Paint.Style.FILL
//设置画笔宽度
it.strokeWidth =50F
}