Kotlin 提供了一些 Extensions 叫 Standard Functions,合理使用 Standard Functions 可以提高代码可读性。

本文主要分析学习下其中的 T.run, T.apply, T.let, T.also, T.takeIfT.takeUnless

T.run & T.apply

直接看 T.run的源码

1
2
3
public inline fun <T, R> T.run(block: T.() -> R): R {
return block()
}

很简短的一个 inline 方法,其中的入参的 T.() -> R 是这个方法的关键,在 Kotlin 里这种用法叫 Function literals with receiver ,通过它 lambda 就可以直接获得一个显式的 this 指向 T 这个对象,所以在 block() 这个 lambda 里的上下文就变成了 T 。

这样通过 T.run 就能简洁地创建一个局部的上下文运行环境了。

T.applyT.run 唯一的不同在于 T.apply 执行完 lambda 后会返回 T ,而 T.run 返回的是 lambda 的执行结果

1
2
3
4
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}

下面用两个例子来简单说明 T.runT.apply 的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 使用 T.run 前
mBinding?.gameList?.(this.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false
mBinding?.gameList?.layoutManager = mLayoutManager
mBinding?.gameList?.adapter = mListAdapter

// 使用 T.run 后
mBinding?.gameList?.run {
(itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false
layoutManager = mLayoutManager
adapter = mListAdapter
}

// 使用 T.apply 前
fun getInstance(openingEntity: OpeningEntity?): OpeningDialog {
val dialog = OpeningDialog()
dialog.mOpeningEntity = openingEntity
return dialog
}

// 使用 T.apply 后
fun getInstance(openingEntity: OpeningEntity?) = OpeningDialog().apply {
mOpeningEntity = openingEntity
}

T.let & T.also

T.letT.also 的关系跟上述类似

1
2
3
4
5
6
7
8
public inline fun <T, R> T.let(block: (T) -> R): R {
return block(this)
}

public inline fun <T> T.also(block: (T) -> Unit): T {
block(this)
return this
}

T.let 返回的是 lambda 的执行结果,T.also 返回的是 T 本身

T.letT.also 都使用了 T 做为 lambda 的入参,所以我们在使用时需要以 it 或重命名的 T 来调用 T 的方法。

T.let 常用于创建局部运行环境,T.also 因为其返回 T 本身则可以用于构建链式调用。

T.takeIf & T.takeUnless

1
2
3
4
5
6
7
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
return if (predicate(this)) this else null
}

public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
return if (!predicate(this)) this else null
}

很好理解,T.takeIf 是当传入的 lambda 返回 true 时返回 T,而 T.takeUnless 则是当传入的 lambda 返回 false 时返回 T。

T.takeUnless 的简单用法

1
2
3
4
fun getInstance(welcomeEntity: WelcomeDialogEntity?) = welcomeEntity
.takeUnless { it?.icon.isNullOrEmpty() } // 当 welcomeEntity 的 icon 为空时返回空
.let { WelcomeDialog() } // 创建 WelcomeDialog
.apply { mWelcomeEntity = welcomeEntity } // 赋值

尾声

你或许会好奇创建这么多 lambda 是否会影响性能,答案是不会,理由是上面描述的方法使用了 inline 关键字。

Refenece