Kotlin標(biāo)準(zhǔn)庫中的apple
/also
/run
/let
這四個(gè)函數(shù)相當(dāng)有意思桥状。它們的實(shí)現(xiàn)非常簡單词疼,區(qū)區(qū)兩三行,卻直擊了Java的若干痛點(diǎn)丸冕。
Kotlin對(duì)它們的定位是scope functions延赌。這是什么意思呢除盏?我理解,scope functions是指這些函數(shù)可以通過新的作用域操作對(duì)象挫以,避免引入臨時(shí)變量或一次性函數(shù)污染原作用域者蠕。
首先,讓我們來看看apply
:
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
寥寥數(shù)行屡贺,信息量卻很大蠢棱。
此函數(shù)有如下幾個(gè)特點(diǎn):
- 使用
inline
:跟C的inline
類似锌杀,Kotlin的inline
也是在編譯期將函數(shù)調(diào)用直接在調(diào)用處展開甩栈,以節(jié)省函數(shù)調(diào)用的開銷;所不同的是糕再,Kotlin還會(huì)把傳入的lambda
函數(shù)也展開量没。 - 結(jié)合泛型和擴(kuò)展,即
T.apply
突想,使得它成為了任意對(duì)象的成員函數(shù) - 唯一的參數(shù)
block
是一個(gè)lambda
函數(shù)殴蹄,且block
的參數(shù)是T.()
究抓,相當(dāng)于為T
擴(kuò)展了一個(gè)無參的成員函數(shù) - 由于
apply
和block
都相當(dāng)于是T
的成員函數(shù),在這兩個(gè)函數(shù)的作用域里的this
都指的是T
的一個(gè)實(shí)例 - 執(zhí)行傳入的
block
袭灯,并返回this
apply
如何使用呢刺下?一個(gè)例子:
class House {
val window = Window().apply {
location = "Living Room"
color = Color.WHITE
size = Size.LARGE
}
}
這個(gè)例子寫成Java將會(huì)是這樣:
class House {
private final Window window = createWindow();
private Window createWindow() {
Window window = new Window();
setLocation("Living Room");
setColor(Color.WHITE);
setSize(Size.LARGE);
return window;
}
}
可以看出,利用apply
我們避免了引入createWindow
這種一次性使用的函數(shù)污染House
的成員稽荧。
(當(dāng)然橘茉,對(duì)于上面的例子,我們可以通過給Window
加一個(gè)Builder
來避免引入額外的函數(shù)createWindow
姨丈。但如果我們還需要調(diào)用setter
以外的方法呢畅卓? )
下面,我們來看看also
:
public inline fun <T> T.also(block: (T) -> Unit): T {
block(this)
return this
}
誒蟋恬?also
和apply
不是一樣的嗎翁潘?非也。這兩個(gè)函數(shù)對(duì)block
的定義不同歼争。之前的apply
中拜马,block
的定義是T.() -> Unit
,可作為T
的擴(kuò)展函數(shù)矾飞;而also
的是(T) -> Unit
一膨,它沒有擴(kuò)展T
,而是把T
作為一個(gè)參數(shù)洒沦。
also
適用于需要把T
傳遞給其它對(duì)象的函數(shù)的場景豹绪。例如:
fun newHouseWithAWindow() {
return House()
.add(Window())
.also { log.debug("Created a new house with a window. house=$it") }
}
而用Java寫的話,我們?yōu)榱四軌騦og返回值申眼,需要引入一個(gè)局部變量:
Window newHouseWithAWindow() {
House house = new House().add(new Window());
log.debug("Created a new house with a window. {}", house);
return house;
}
最后是run
和let
:
public inline fun <T, R> T.run(block: T.() -> R): R = block()
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
run
與apply
類似瞒津,let
與also
類似。所不同的是括尸,這兩個(gè)函數(shù)在執(zhí)行完block
之后巷蚪,返回的是block
的返回值,而非this
濒翻。
總而言之屁柏,這四個(gè)函數(shù)的異同點(diǎn)在于:
- 傳入的
block
的作用域里的是this
還是it
- 返回值是
this
還是block
的執(zhí)行結(jié)果
函數(shù) | block里 | 返回值 |
---|---|---|
apply | this | this |
also | it | this |
run | this | block的結(jié)果 |
let | it | block的結(jié)果 |
那么,怎么選擇應(yīng)該用哪個(gè)函數(shù)呢有送?Kotlin官方文檔Coding Conventions中有一節(jié)Using scope functions apply/with/run/also/let對(duì)此已有解答淌喻,這里不再贅述。