前言
Kotlin 标准库包含几个函数,它们的唯一目的是在对象的上下文中执行代码块。当对一个对象调用这样的函数并提供一个lambda 表达式时,它会形成一个临时作用域。在此作用域中,可以访问该对象而无需其名称。这些函数称为作用域函数。共有以下五种:
let
、run
、with
、apply
以及also
。
以上是官网对Kotlin作用域的一个解释,读几遍是不是也不懂他意思?
从实际出发,依次展开来看看。
let
在作用域中使用(it
)来访问对象,返回值是lambda 表达式的结果。
fun main() {
var name="name"
println(name.let {
it.length
})
}
复制代码
如上操作,在{}号中,有个隐式的变量名it,在其中对name变量的任何操作都可以使用it来代替,如果在其中更改了对象的值,那么it变量是不会发生改变的。
并且最后一行的返回结果,会作为let函数的返回结果。如果最后一行没有返回值,在Kotlin中也会被kotlin.Unit来代替。
run
在作用域中使用(this
)来访问对象,返回值是lambda 表达式的结果。
fun main() {
var name="name"
println(name.run {
println(length)
"a"
})
}
复制代码
如上操作,除了返回值和let一样,他的作用域变量成为了this,但是这里就发生了一个变化,对对象的操作可以不加this,直接可以调用,而let中不行,比如上面的操作,如果用let来访问length方法时,就必须是it.length,在看一个例子,
class Data(var name: String) {
}
fun main() {
var name = "name"
println(Data("dog").run {
println(name)
name="cat"
})
println(name)
}
复制代码
run还有一个非扩展方法,调用也不需要什么参数,返回值Lambda 表达式结果,如下。
fun main() {
var function = {
println("a")
}
// function()
run(function)
}
复制代码
在Java中,方法中写的{}会自动执行,而kotlin不会,需要手动调用,或者是run。
with
他不是扩展函数,也就是没有obj.with这种写法,而是with(obj){},然后在lambda表达式内部,可以使用(this
)访问此对象,也可以省略, 返回值是lambda 表达式结果。
with可以理解为"对于这个对象,执行以下操作"。
var name="cat"
with(name){
name="dog"
println(name)
println(this)
}
复制代码
同样,在作用域中对原对象修改后,并不影响其中的this。
apply
在作用域中使用(this
)来访问对象,返回值是是对象本身。
class Data(var name: String) {
}
fun main() {
var name = Data("dog").apply {
name = "cat"
}
println(name.name)
}
复制代码
also
在作用域中使用(it
)来访问对象,返回值是是对象本身。
class Data(var name: String) {
}
fun main() {
var name = Data("dog").also {
it.name="cat"
}
println(name.name)
}
复制代码
总结
看了这么多,是不是还迷迷糊糊的,这些作用域函数都非常相似,因此了解它们之间的区别很重要,我们可以从他们这两点区分。
- 引用上下文对象的方式
- 返回值
函数 | 对象引用 | 返回值 | 是否是扩展函数 |
---|---|---|---|
let |
it |
Lambda 表达式结果 | 是 |
also |
it |
上下文对象 | mps |
run |
this |
Lambda 表达式结果 | 是 |
with |
this |
Lambda 表达式结果 | 不是:把上下文对象当做参数 |
apply |
this |
上下文对象 | 是 |
run |
- | Lambda 表达式结果 | 不是:调用无需上下文对象 |
最重要的就是记住返回值,因为对象引用的方式不太重要,如果你想少写代码,并且返回值需要Lambda 表达式结果,那么就使用run,如果需要本身,那就使用apply。
我们在看一个例子。
public class MainJava {
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("/home/HouXinLin/1.txt")));
String temp = null;
while ((temp = bufferedReader.readLine()) != null) {
System.out.println(temp);
}
}
}
复制代码
而在Kotlin中while中又赋值又判断这种写法是错的,解决方式就需要使用作用域函数,并且接收的变量还需要设置为可空类型
fun main() {
var bufferedReader = File("/home/HouXinLin/1.txt").inputStream().bufferedReader()
var temp: String?
while (bufferedReader.readLine().also { temp = it } != null) {
println(temp)
}
}
复制代码