The Kotlin standard library contains several functions whose sole purpose is to execute a block of code within the context of an object. When you call such a function on an object with a lambda expression provided, it forms a temporary scope. In this scope, you can access the object without its name. Such functions are called scope functions.
简单理解,在上下文对象范围 (大括号) 内,可以不用对象的名字,直接调用
Person("Alice", 20, "Amsterdam").let {
println(it)
it.moveTo("London")
it.incrementAge()
println(it) // 这里是Unit
}
正常写
val alice = Person("Alice", 20, "Amsterdam")
println(alice)
alice.moveTo("London")
alice.incrementAge()
println(alice)
The scope functions do not introduce any new technical capabilities, but they can make your code,more concise and readable.
scope functions 使得代码更加简洁易读
val adam = Person("Adam").apply {
age = 20 // same as this.age = 20 or adam.age = 20
city = "London"
}
println(adam)
这是不是比设计模式中builder模式更加简单,创建对象更加简洁
fun getRandomInt(): Int {
return Random.nextInt(100).also {
writeToLog("getRandomInt() generated value $it")
}
}
val i = getRandomInt()
做额外操作,如日志,埋点
val numbers = mutableListOf("one", "two", "three")
val countEndsWithE = numbers.run {
add("four")
add("five")
count { it.endsWith("e") } //返回count计数的结果
}
println("There are $countEndsWithE elements that end with e.")
run is useful when your lambda contains both the object initialization and the computation of the return value.
同时进行对象初始化和返回计算结果时推荐使用
val hexNumberRegex = run {
val digits = "0-9"
val hexDigits = "A-Fa-f"
val sign = "+-"
Regex("[$sign]?[$digits$hexDigits]+")
}
for (match in hexNumberRegex.findAll("+1234 -FFFF not-a-number")) {
println(match.value)
}
将相同功能代码聚在一起
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
println("'with' is called with argument $this")
println("It contains $size elements")
}
官网建议不要返回lambda 结果,即返回Unit
val service = MultiportService("https://example.kotlinlang.org", 80)
val result = service.run {
port = 8080
query(prepareRequest() + " to port $port")
}
// the same code written with let() function:
val letResult = service.let {
it.port = 8080
it.query(it.prepareRequest() + " to port ${it.port}")
}
官网的不同方法差异
方法 | 引用对象 | 返回值 | 是否扩展函数 |
---|---|---|---|
let |
it |
Lambda 结果 | Yes |
run |
this |
Lambda 结果 | Yes |
kotlin.run |
- | Lambda 结果 | No: 调用不需要上下文对象 |
with |
this |
Lambda 结果 | No: 上下文对象作为参数 |
apply |
this |
上下文对象 | Yes |
also |
it |
上下文对象 | Yes |
简化成下图
简单调用总结
//都用过?.let
obj?.let{
}
//其实?.run 也行,省略上下文,但是注意不要跟局部或者成员变量的属性搞混了
obj?.run{
}
其实可以混用,但是为了可读性,限制使用场景
takeIf
and takeUnless
这两的扩展也是stardartKt里的,官方kotlon-docs也放在scope funciton 里介绍的存在的目的就是更好的进行链式调用 (checks of the object state in call chains)
val number = Random.nextInt(100)
val evenOrNull = number.takeIf { it % 2 == 0 }
val oddOrNull = number.takeUnless { it % 2 == 0 }
println("even: $evenOrNull, odd: $oddOrNull")
一般是由都是搭配elvis表示式使用
//变量
val showName = name.takeIf{ it.isNullOrEmpty()} ?: "default"
//代码块 , 可以取代 if else , 一链到底是不是很爽
name.takeIf{ it.isNullOrEmpty()}
?.let {
//满足条件时执行
} ?:kotlin.run {
//不满足条件时执行
}
以上是个人的使用经验,如有问题或者更好的,欢迎留言.
Using Scoped Functions in Kotlin - let, run, with, also, apply
kotlin-docs scope-functions