
在 Kotlin 源码的
Standard.kt
标准库中提供了一些便捷的内置高阶函数(let
、also
、with
、run
、apply
),可以帮助我们写出更简洁优雅的 Kotlin 代码,提高开发效率。但前提是先要对高阶函数有所了解。
// 比如这样初始化一个User
// 这样的代码是不是简洁优雅,我是比较喜欢这种风格的
val user = User().apply {
this.name = "kotlin"
this.age = 18
this.isMale = true
}
1、apply
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
- 传递
this
作为block函数参数(调用时可以省略),且apply
函数的返回值是调用者本身 - 实际应用:Fragment的
newInstance()
方法传递参数时
fun newInstance(param: String) {
return DemoFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM, param)
}
}
}
2、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
}
- 传递
it
作为block函数参数(调用时不可以省略),且also
函数的返回值是调用者本身 - 实际应用:
apply
与also
之间可以相互替换,只是参数不同而已
3、let
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
- 传递
it
作为block函数参数,且let
函数的返回值是由block函数决定的 - 实际应用:在Activity或Fragment传参时
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param = it.getString(ARG_PARAM)
}
}
4、with
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
- 需要传递两个参数,需要操作的对象和block函数
- 传递
this
作为block函数参数,且with
函数的返回值是由block函数决定的 - 实际应用:
with(recyclerView) {
layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
adapter = mAdapter
}
5、run
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
- 第一个
run
就是一般的高阶函数,为了能用Lambda表示,实际应用比较少 - 传递
this
作为block函数参数,且run
函数的返回值是由block函数决定的 - 实际应用:
run
与let
之间可以互相替换,只是传递参数不同
但是大家在平时实际开发中是不是有这种感觉,这么多内置函数我该选哪一个呢?反正我在刚开始接触标准库时,我是这种感觉,一脸懵逼。。。
但在后来开发中,慢慢熟悉过后,发现其实也有规律可循,可参考顶图。
- 如果需要返回自身调用者本身(即
return this
),可以选择apply
also
- 如果需要传递this作为参数,可以选择
apply
run
with
- 如果需要传递it作为参数,可以选择
let
also
- 如果返回值需要函数决定(即
return block()
),可以选择run
with
let
扩展:利用标准函数库实现自己的高阶函数
/**
* 使用TypedArray可以不用自己recycle
*/
inline fun <R> TypedArray.use(block: (TypedArray) -> R): R {
return block(this).also {
recycle()
}
}
/**
* 使用Bitmap可以不用自己recycle
*/
inline fun <R> Bitmap.use(block: (Bitmap) -> R): R {
return block(this).also {
recycle()
}
}
6、takeif
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
}
我们可以通过一个对象来调用它,如果predicate函数返回值为true,则返回调用对象,否则返回null,注意predicate函数的参数就是当前调用对象。
这其实就是一个加强版的if表达式,更加灵活,我们可以让对象使用安全调用操作符?.,由于 takeIf 函数可以返回对象本身,那么自然可以进行链式调用。写个例子简单比较下:
fun filterUser1(user: User?) {
if (user != null && user.age > 18 && user.sex == "male") {
println(user.toString())
}
}
fun filterUser2(user: User?) {
user?.takeIf {
it.age > 18 && it.sex == "male"
}.apply {
println(toString())
}
}
7、takeUnless
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
}
嗯?如果predicate函数返回值为false,则返回调用对象,否则返回null,功能和 takeIf 函数相反!
8、repeat
repeat 函数声明如下:
@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
contract { callsInPlace(action) }
for (index in 0 until times) {
action(index)
}
}
就是将action函数执行times次,函数的参数就是当前的次数:
fun main(args: Array<String>) {
repeat(6) {
println("Kotlin$it")
}
}
// 输出
Kotlin0
Kotlin1
Kotlin2
Kotlin3
Kotlin4
Kotlin5
9、TODO
TODO 函数的声明如下:
@kotlin.internal.InlineOnly
public inline fun TODO(): Nothing = throw NotImplementedError()
@kotlin.internal.InlineOnly
public inline fun TODO(reason: String): Nothing = throw NotImplementedError("An operation is not implemented: $reason")
和 Java 中的TODO类似,可以用来标注某个方法需要重写,或者没有完成的事项等等,但是 Kotlin 的 TODO 会抛出异常,并可以指定异常原因!
网友评论