参考文章
概述
这5个作用域函数,包括通用的扩展函数和普通函数,它们的唯一目的是在对象的上下文中执行代码块。当对一个对象调用这样的函数并提供一个 lambda 表达式时,它会形成一个临时作用域。在此作用域中,可以访问该对象而无需其名称——即扩展函数的作用。
典型用法:
Person("Alice", 20, "Amsterdam").let {
println(it)
it.moveTo("London")
it.incrementAge()
println(it)
}
如果不使用 let 来写这段代码,就必须引入一个新变量,并在每次使用它时重复其名称。
val alice = Person("Alice", 20, "Amsterdam")
println(alice)
alice.moveTo("London")
alice.incrementAge()
println(alice)
区别:
由于作用域函数本质上都非常相似,每个作用域函数之间有两个主要区别:
- 引用上下文对象的方式
- 返回值
调用方式
run函数是唯一一个可以直接使用的:run{}
with函数是唯一一个以传入参数的方式调用的:with(T){}
作用域
this作用域:run、with、apply
- 作为 lambda 表达式的接收者
it作用域:let、also
- 作为 lambda 表达式的参数
- 当将上下文对象作为参数传递时,可以为上下文对象指定在作用域内的自定义名称
fun getRandomInt(): Int {
return Random.nextInt(100).also { value ->
writeToLog("getRandomInt() generated value $value")
}
}
val i = getRandomInt()
返回值
返回程序执行的结果:run、let、with
- 使用其结果给一个变量赋值
- 忽略返回值,仅使用作用域函数为变量创建一个临时作用域
返回调用者本身:also、apply
- 可以作为辅助步骤包含在作用链中
- 可以用在return 语句中
let
- 上下文对象作为 lambda 表达式的参数(
it)来访问。
- 返回值是 lambda 表达式的结果,即最后一行
T.let{}
fun main() {
val str = "Hello"
str.let {
println("The receiver string's length is ${it.length}")
}
}
with
- 非扩展函数
- 上下文对象作为参数传递,但是在 lambda 表达式内部,作为接收者(
this)使用
- 返回值是 lambda 表达式的结果,即最后一行
with(T){ }
with可以理解为“对于这个对象,执行以下操作” :
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
println("'with' is called with argument $this")
println("It contains $size elements")
}
with的另一个理解是引入一个辅助对象,其属性或函数将用于计算一个值:
val numbers = mutableListOf("one", "two", "three")
val firstAndLast = with(numbers) {
"The first element is ${first()}," +
" the last element is ${last()}"
}
println(firstAndLast)
run
- 上下文对象 作为接收者(
this)来访问 - 返回值 是 lambda 表达式结果
run{ }
用作非扩展函数,作为一个具有独立作用域的代码块进行一些操作,并返回该代码块最后执行的结果。
//这里str = "abc"
val str = run {
1
2
"abc"
}
//在实际开发中可能会这样用
//比如如果用户登录了就弹奖励弹窗,没有登录就弹去登录弹窗
run {
if(isLogin) {
rewardDialog("去领奖")
}else {
return@run loginDialog("去登录“)
}
}.show()
T.run{ }
run 和 with 做同样的事情,但是调用方式和 let 一样——作为上下文对象的扩展函数,并返回最后的结果。
val service = MultiportService("https://example.kotlinlang.org", 80)
val result = service.run {
port = 8080
query(prepareRequest() + " to port $port")
}
// 同样的代码如果用 let() 函数来写:
val letResult = service.let {
it.port = 8080
it.query(it.prepareRequest() + " to port ${it.port}")
}
apply
- 上下文对象作为 lambda 表达式的参数(
it)来访问 - 返回值是调用者对象本身。
T.apply{ }
可以理解为“将以下赋值操作应用于对象”。
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
also
- 上下文对象作为 lambda 表达式的参数(
it)来访问 - 返回值是上下文对象本身。
T.also{ }
- 可以将其理解为“并且用该对象执行以下操作”。
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
补充:takeIf 、takeUnless
takeIf 函数和 takeUnless 函数允许在链式调用中加入对象的状态检查。
在写if语句的时候会遇到这样的场景:前面调用了一个函数计算得出了一个结果,现在需要对这个结果做一个分支判断,并且我们只需要用到if的一个分支时,可以用takeIf和takeUnless代替
fun testWithoutTakeIf() {
val name = "test"
val t = name.indexOf("t")
Log.i(TAG, "testWithoutTakeIf: t = $t")
if (hasYan >= 0) {
Log.i(TAG, "testWithoutTakeIf: has t")
}
Log.i(TAG, "testWithoutTakeIf: $name")
}
输出:
I: testWithoutTakeIf: t = 0
I: testWithoutTakeIf: has t
I: testWithoutTakeIf: test
可以用takeIf这样写:
fun testTakeIf() {
val name = "test"
name.indexOf("t")
.takeIf {
Log.i(TAG, "testTakeIf: it = $it")
it >= 0
}
?.let {
Log.i(TAG, "testTakeIf: has t")
}
Log.i(TAG, "testTakeIf: $name")
}
输出:
I: testTakeIf: it = 0
I: testTakeIf: has t
I: testTakeIf: test
takeIf
- 是扩展函数
- 上下文对象的引用方式:it
- 返回值:如果代码块predicate里面返回为true,则返回这个对象本身,否则返回空
- 使用注意:结果要用?判空
takeUnless
- 是扩展函数
- 上下文对象的引用方式:it
- 返回值:如果代码块predicate里面返回为false,则返回这个对象本身,否则返回空
- 使用注意:结果要用?判空