目录
一、简介
二、let
三、with
三、run
四、apply
五、also
六、 run的两种方式为什么this作用域不一样,一个是 调用该函数所在的实例,一个是T对象本身实例
七、为什么let 和 also使用it代替本身,而 with、run和apply 使用this 或省略代表本身
八、五个方法对比和比较
Standard.kt是Kotlin库的一部分,它定义了一些基本函数,主要包括五个常用方法:run、with、apply、also、let
看下 let的源码
@kotlin.internal.InlineOnly
public inline fun T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
定义了一个T类型对象,可以调用let方法,参数传了一个lambda函数,该参数block为 T类型参数,返回值是R类型, 然后fun let 返回值类型也是R。
根据lamda语法特点,可以用it代指对象本身,返回值为函数块的最后一行或指定return表达式。
总结就是 使用方式为
(1) 不可为空变量
var t:T = T()
t.let{
it.a()
it.b()
it.c()
}
(2) 可为空变量
var t:T? = null
t?.let{
it.a()
it.b()
it.c()
}
(3) 返回值 为代码块最后一句 或者指定 return
@JvmStatic
fun main(args: Array) {
val result = "let".let {
println(it.length)
"let"
}
println(result)
}
运行结果
3
let
(4)最常用的是 可空变量 多个操作处理,不用判断空异常
(5)特点:let函数 用it代表调用对象本身,不能使用this或省略,返回值为函数体最后一句或者return指定返回
看下with源码:
@kotlin.internal.InlineOnly
public inline fun with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
与let不同的是,let定义的是 T.let, 这里没有定义 T.with,而是定义with,传参是 T对象本身 和 lamda函数 block,根据lamda简化规则,方法最后一个参数是lamda表达式,且唯一,可以把表达式拿出去。所以一般使用如下
with(loginData){
println(this?.token)
println(this?.userId)
println(this?.userName)
}
可以调用this,这里 loginData是可空变量,如果非可空,还可以省略this,所以with有个缺点就是如果传入对象为可空对象比较麻烦,没有空处理,后面也是lamda表达式和let一样 返回值也是 函数体最后一句或者是return指定
(1)使用场景:用于调用同一个类的多个方法时,如果比较懒连it都不想写,可以省去类名重复,直接调用类的方法就行,经常用于适配器中,例如RecycleView的onBinderViewHolder中。
代码示例:
orderInfo?:return
with(orderInfo){
helper.setText(R.id.tv_order_total_money, orderPrice.toString())
helper.setText(R.id.tv_order_total_discounts_money, presentPrice.toString())
helper.setText(R.id.tv_order_total_real_money, actualPrice.toString())
}
(2) 特点:with函数需要传对象, 用this或省略代表对象本身,没有做空异常处理,返回值为函数体最后一句或者return指定返回
看下run的源码
@kotlin.internal.InlineOnly
public inline fun run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
@kotlin.internal.InlineOnly
public inline fun T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
有两个方法,直接调用 run 代表作用对象为外部类对象,T.run 代表作用对象为T类型对象。
第一个方法:
var name = "123"
println(name)
run{
name = "234"
println(name)
}
println(name)
结果是:
123
234
234
第二个方法
var loginData:LoginData?=null
loginData?.let {
it.token = "1jdifjidfjidfj"
it.userId = "1001"
it.userName = "123"
}
loginData?.run {
token = "1jdifjidfjidfj"
userId = "1001"
userName = "123"
}
run可以像let一样有使用空判断
run结合了 let和with两者特点,所以 能用let 和 with写的都能用 run写,run也是可以用this和省略代指调用对象本身,可以省略,然后返回值类型也是函数体 最后一句 或retrun指定
(1)使用场景:let和with使用的所有场景
(2)特点:run函数是let和with的结合体, 用this或省略代表对象本身,可以做空异常处理,返回值为函数体最后一句或者return指定返回
看源码:
@kotlin.internal.InlineOnly
public inline fun T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
可以看到 最后有指定返回值 this,返回本身,所以apply和 上面let、with、run 在返回上面有明显区别,然后传参是一个 无返回值lambda表达式,表达式和run表达式类似,也是用this或者省略代指本身。
(1)使用场景:
需要对对象本身做一些操作,然后返回对象本身,对象实例初始化,需要对对象属性初始化赋值,或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。
bottomView = View.inflate(activity, R.layout.view_bottom, null).apply{
tv_title = "123"
tv_name = "djfidfj"
seek_bar.max = 100
seek_bar.progress = 0
}
需要做多级判空处理:
(2)总结:大体与run相似,也是this或者省略代表本身,可以做空异常处理,返回值为本身,可以做链式调用处理
also源码:
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
我们发现also和let很相似,最后都是用it代表对象本身,只不过返回值不一样,let是返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。also是返回本身
(1)使用场景:多个扩展函数链式调用
(2)总结:大体与let相似,也是it代表本身,可以做空异常处理,返回值为本身,可以做链式调用处理
我们来看看两种方式源码有啥区别:
@kotlin.internal.InlineOnly
public inline fun run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
@kotlin.internal.InlineOnly
public inline fun T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
这里可以看到 一个是() ->R, 一个是 T.() ->R,
所以回调函数里面 this一个是代表调用run方法的实例,
另一个函数里面this作用域是 类型T的实例
我们来看看源码区别:
let和also 中lambda表达式是:
block: (T) -> R 和 block: (T) -> Unit
前面都是传 (T)
而with、run和apply中的lambda表达式是:
block: T.() -> R、block: T.() -> R和 block: T.() -> Unit
前面都是传 T.()
一个是传(T),一个是传T.()这个区别
只有T.() 这样才代表 回调方法里面this作用域为 T对象本身,而this又可以省略。而(T) 这样 回调方法里面的不能用this代表T对象本身,所以用一个t指代 T对象本身
名称 | 源码 | 函数体内什么代表本身 | 返回值 | 是否是扩展函数 | 适用的场景 |
---|---|---|---|---|---|
let | @kotlin.internal.InlineOnly public inline fun |
it | 闭包形式返回 | 是 | 适用于处理不为null的操作场景 |
with | @kotlin.internal.InlineOnly public inline fun |
this或省略 | 闭包形式返回 | 否 | 适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上 |
run | (1) @kotlin.internal.InlineOnly public inline fun (2) @kotlin.internal.InlineOnly public inline fun |
this或省略 | 闭包形式返回 | 是 | 适用于let,with函数任何场景。 |
apply | @kotlin.internal.InlineOnly public inline fun |
this或省略 | 返回this | 是 | 1、适用于run函数的任何场景,一般用于初始化一个对象实例的时候,操作对象属性,并最终返回这个对象。 2、动态inflate出一个XML的View的时候需要给View绑定数据也会用到. 3、一般可用于多个扩展函数链式调用 4、数据model多层级包裹判空处理的问题 |
also | @kotlin.internal.InlineOnly @SinceKotlin("1.1") public inline fun |
it | 返回this | 是 | 适用于let函数的任何场景,一般可用于多个扩展函数链式调用 |
参考文档:https://blog.csdn.net/u013064109/article/details/78786646