Kotlin run、with、apply、also、let、takeIf、takeUnless、repeat的使用及區別
文章目錄
網上有關於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()
}