文章目录
网上有关于run、with、apply、also、let、takeIf、takeUnless、repeat的使用及区别并且已经总结好了,但通过死记硬背用不了多久我们就会忘记,而且由于这几个函数比较像,我们很容易弄混了,下面我们通过分析源代码的角度去学习这个函数的使用及区别。下面我们以run来举例:
下面1、2、3对应上图红色方框1、2、3处
1、T.run 表示:此函数是扩展函数,任意对象都可调用。
2、接收一个函数,并且函数内任意调用改对象的属性、方法
3、返回函数内的return
我们对这几处的标记都清楚后我们具体看看run、with、apply、also、let、takeIf、takeUnless、repeat的功能。
run
/**
* Calls the specified function [block] and returns its result.
*/
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
/**
* Calls the specified function [block] with `this` value as its receiver and returns its result.
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
run和T.run的用法如下:
val person = Person()
person.run {
age = 1
sex = 3
}
var e = run{
var a =""
2+3
}
class Person(var name: String = "", var age: Int = 0, var sex: Int = 0)
T.run是扩展函数,任何对象都可以调用,大括号内可以直接调用对象的属性和方法。
run:返回大括号内最后一行
注:有的文章中说可以使用return返回,如下:
var e = run{
var a =""
return 2+3
}
但我这无法编译通过,有可能是版本的问题。
with
/**
* Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
我们发现with也不是扩展函数,而且有2个参数,返回block函数的返回值
用法:
with(person) {
age = 1
this.sex = 3
}
1、block函数内this代表with的参数,所以可以直接调用persion的属性、方法
2、从用法上我们发现persion不能为null
3、this可以省略
apply
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
1、apply返回的是对象本身
2、block函数内this代表对象本身
使用:
val newPersion = person.apply { this.age = 0}
.apply { sex =2 }
是不是感觉可以替代Builder模式
also
@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
}
和apply对比发现是block的参数不一样,apply是T.(),also是(T),所以使用是不同的:
val b = person.also {it.age =4}
.also { it.sex = 5 }
also中不可以直接调用对象的属性和方法,通过it调用,it代表当前对象
let
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
和also对比不同是返回值不同,also返回当前对象,let返回block函数最后一行
takeif
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (predicate(this)) this else null
}
如果函数返回值是true则返回对象本身,否则返回null
使用:
var p = person.takeIf { true }
takeUnless
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (!predicate(this)) this else null
}
takeUnless和takeif相反,使用:
var p = person.takeUnless { true }
repeat
@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
contract { callsInPlace(action) }
for (index in 0 until times) {
action(index)
}
}
就是封装了一个for循环,使用:
repeat(4){
//it代表 0..3
print(it)
}
总结
看完上面的是不是特别晕,感觉没什么区别啊,下面总结下他们的区别和相同点,我觉得没必要背下来,我们需要通过源码理解他的意思。
1、他们都是作用域函数,都提供了内部作用域
2、函数前没有T的是普通函数(run,with)和有T的是扩展函数(T.run,T.also 等)
3、参数不同
block:T.() 参数当前对象,this可以省略
blcok:(T): 参数当前对象,it表示当前对象,不可省略
无参数的形式:block: ()
4、返回值不同:
return block():返回block函数最后一行
return this:返回当前对象
使用场景
具体代码中的使用
1、属性设置(包括自定义类和系统对象)
原来的写法:
var person = Person()
person.age = 1
person.name = ""
person.sex = 4
let写法 :
person.let {
it.name = ""
it.age = 1
it.sex = 3
}
run写法(推荐):
person.run {
name = ""
age = 1
sex = 3
}
系统属性:
textView.run {
textSize = 12F
text = ""
}
是不是run的写法更“漂亮”。
2、需要null判断的地方
if(null != textView){
print("textView is not null")
add()
}
run、let写法:
textView?.let {
print("textView is not null")
add()
}
textView?.run {
print("textView is not null")
add()
}