详解Kotlin操作符:T.apply、T.let、T.run、T.also、with、run
乍看这6个操作符,挺唬人的,又要花费不少脑细胞去理解和区分,没办法,只能这样。希望看完这片这篇文章能帮到你。
First things first,来研究下run和with这两个操作符,为啥先他俩呢?难道你没发现就他俩调用不需要变量,而其余四个则需要一个变量T才能调用?
with
先看看标准库里的源码:
public inline fun with(receiver: T, block: T.() -> R) : R = receiver.block()
一眼就能明白,with就是一个内敛函数且接收两个参数,talk is cheap:
classJujube{
fun violet(){
val str="Jujube"
val result=with(str,{
println(this)// 接收者
// println(it) // 参数 with操作符不能识别it
68//区间返回值
})
println(result)
}
}
fun main(vararg args:String){
Jujube().violet()
}
先说明:上面的代码不规范。由于with的第二个参数是最后一个参数而且类型是lambda,那么可以挪到小括号外面(称之为裸奔的lambda),那么上面的代码规范的写法就是如下:
classJujube{
fun violet(){
val str="awegerg"
val result=with(str){
println(this)// 接收者
// println(it) // 参数 with操作符不能识别it
68//区间返回值
}
println(result)
}
}
fun main(vararg args:String){
Jujube().violet()
}
打印结果是
awegerg
68
就这么多,wiht操作符是不是很简单?
run
先看看标准库里的源码:
public inline fun T.run(block:T.()->R):R=block()
run同样是一个内敛函数,但是相对于with来说,run只接收一个lambda参数,这是第一点区别。因此套用之前的代码的话,会出现下面的代码:
classJujube{
fun violet(){
val str="awegerg"
val result=run{
println(this)// 接收者
// println(it) //
68//区间返回值
}
println(result)
}
}
fun main(vararg args:String){
Jujube().violet()
}
打印结果是
com.htgames.nutspoker.debug.fo.Jujube@1540e19d
68
根据打印结果你能看出run和with的第二点区别了吗?相信你应该能,就是他俩的this指向的引用不同,with中的this指向的是它的第一个参数,而run中的this指向的是对象实例。with和run大概就这两点区别吧。
T.let
源码:
publicinlinefunT.let(block:(T)->R):R=block(this)
接收一个lambda参数而已。
classJujube{
fun violet(){
val str="awegerg"
val result=str.let{
println(this)// 接收者
println(it)
68//区间返回值
}
println(result)
}
}
fun main(vararg args:String){
Jujube().violet()
}
打印结果是
com.htgames.nutspoker.debug.fo.Jujube@1540e19d
awegerg
68
把打印结果用表格显示更清晰:
使用T.run、T.apply、T.also套用上面的代码,用表格显示它们的打印结果如下:
结合之前介绍的with和run,那么最终的表格如下:
Second things second,我在项目开发中用的比较多的是with和T.apply,用的多了就明白它们的区别了。但是到底如何对这6个操作符进行抉择,我无耻的盗用一张很有用的图(貌似地址需要翻墙),相信能帮到你:
图片来自https://android.jlelse.eu/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84
Third things third, 小技巧:使用T.let和T.apply可以对一个变量进行集体操作,如果需要对变量进行非空检查,那么直接在T后面加上?符号即可,避免了Java中泛滥的if-else的非空语句判断,stop shitting,show you the code below:
vartextView:TextView?=TextView(this)
textView?.apply{//对textView进行集体操作
this.text="a"
this.isClickable=true
this.setOnClickListener{Toast.makeText(this@MainActivity,"click",Toast.LENGTH_SHORT).show()}
}
Last things last, 小技巧:使用run操作符可以对语句添加标签。不知道你在开发中是否遇到这种情景:想要在forEach语句中使用break或者continue。那么问题来了,android studio编辑器会报“break or continue jumps across a function or a class boundary”,这时候run就可以大显神通了,代码如下:
fun main(vararg args:String){
var sameItems = ArrayList()
run run@{//添加标签
for(i in0until6){
varhasSame=false
run outer@{//添加标签
(4..6).forEach dangerous@{
if(it==i){
hasSame=true
sameItems.add(it)
return@outer//返回到outer标签,效果相当于break
}
}
}
//do something
}
}
}
有任何疑问请联系:
owl@violetpersimmon.com