Kotlin标准库中的apple
/also
/run
/let
这四个函数相当有意思。它们的实现非常简单,区区两三行,却直击了Java的若干痛点。
Kotlin对它们的定位是scope functions。这是什么意思呢?我理解,scope functions是指这些函数可以通过新的作用域操作对象,避免引入临时变量或一次性函数污染原作用域。
首先,让我们来看看apply
:
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
寥寥数行,信息量却很大。
此函数有如下几个特点:
- 使用
inline
:跟C的inline
类似,Kotlin的inline
也是在编译期将函数调用直接在调用处展开,以节省函数调用的开销;所不同的是,Kotlin还会把传入的lambda
函数也展开。 - 结合泛型和扩展,即
T.apply
,使得它成为了任意对象的成员函数 - 唯一的参数
block
是一个lambda
函数,且block
的参数是T.()
,相当于为T
扩展了一个无参的成员函数 - 由于
apply
和block
都相当于是T
的成员函数,在这两个函数的作用域里的this
都指的是T
的一个实例 - 执行传入的
block
,并返回this
apply
如何使用呢?一个例子:
class House {
val window = Window().apply {
location = "Living Room"
color = Color.WHITE
size = Size.LARGE
}
}
这个例子写成Java将会是这样:
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
这种一次性使用的函数污染House
的成员。
(当然,对于上面的例子,我们可以通过给Window
加一个Builder
来避免引入额外的函数createWindow
。但如果我们还需要调用setter
以外的方法呢? )
下面,我们来看看also
:
public inline fun <T> T.also(block: (T) -> Unit): T {
block(this)
return this
}
诶?also
和apply
不是一样的吗?非也。这两个函数对block
的定义不同。之前的apply
中,block
的定义是T.() -> Unit
,可作为T
的扩展函数;而also
的是(T) -> Unit
,它没有扩展T
,而是把T
作为一个参数。
also
适用于需要把T
传递给其它对象的函数的场景。例如:
fun newHouseWithAWindow() {
return House()
.add(Window())
.also { log.debug("Created a new house with a window. house=$it") }
}
而用Java写的话,我们为了能够log返回值,需要引入一个局部变量:
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
类似。所不同的是,这两个函数在执行完block
之后,返回的是block
的返回值,而非this
。
总而言之,这四个函数的异同点在于:
- 传入的
block
的作用域里的是this
还是it
- 返回值是
this
还是block
的执行结果
函数 | block里 | 返回值 |
---|---|---|
apply | this | this |
also | it | this |
run | this | block的结果 |
let | it | block的结果 |
那么,怎么选择应该用哪个函数呢?Kotlin官方文档Coding Conventions中有一节Using scope functions apply/with/run/also/let对此已有解答,这里不再赘述。
网友评论