作用域函数
含义:Kotlin中对一个对象进行一系列操作的函数,写法上类似集合的操作符
种类:run
、let
、also
、apply
、takeIf
、takeUnless
、with
、repeat
第一类:有返回结果的
举例:通过let
和run
函数输入一段话,let
和run
函数都是将参数中闭包的返回值当做自己的返回值给返回出来,具体看源码实现。
// 都是有返回结果,但是let闭包有参数,可以用it指代对象
// run没有闭包参数的,只能用this来指代对象
val letResult = user.let { "let::{${it.javaClass}}" }
println(letResult)
// let::{class lambda.User}
val runResult = user.run { "run::{${this.javaClass}}" }
println(runResult)
// run::{class lambda.User}
let
和run
的源码:
// let
kotlin
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
// run
tlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
通过源码可以看出,let
参数block
是有返回值的。run
同理。但是let
中是将T
继续以参数的形式传给了block
闭包,所以在let
闭包中可以使用it
来代替对象,而run
却只能用this
关键字。
第二类:无返回结果
举例:同样是通过also
和apply
输出一句话,但是没有返回的是对象,不是闭包函数。具体看下面源码实现。
// also、apply和let、run的区别在于闭包内的操作没有返回结果
// also和let一致,有闭包参数,可以使用it
// apply和run一致,没有闭包参数,只能使用this
user.also {
println("also::{${it.javaClass}}")
}
// also::{class lambda.User}
user.apply {
println("apply::{${this.javaClass}}")
}
// apply::{class lambda.User}
源码:
// also
kotlin
@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
}
// apply
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
从also
的源码一眼就能看到,block
参数返回了一个Unit
,函数是将调用者T
给返回了。apply
同理。it
和this
解释看上面let
和run
的解释。
第三类:函数的闭包有返回值,但是为Boolean类型
举例:通过takeIf
和takeUnless
函数中的闭包结果进行判断,然后做出相应的操作。其中闭包返回值都为Boolean类型。
// takeIf和takeUnless的闭包参数都会返回一个Boolean类型的值
// takeIf内闭包为false时,taleIf执行完会返回一个null
// takeUnless内闭包为true时,takeUnless执行完会返回一个null,和takeIf正好相反
user.takeIf { it.name.isNotEmpty() }
?.also { println("name is {${it.name}}") }
?: println("name is null")
user.takeUnless { it.name.isNotEmpty() }
?.also { println("name is null") }
?: println("name is {${user.name}}")
源码:
@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
}
通过上面takeIf
的源码if (predicate(this)) this else null
可以看出,如果闭包的返回值为false
,那么函数就会返回一个null
。
takeUnless
正好与之相反。
第四类:循环执行函数
repeat(2) {
println("count is $it,name is ${user.name}")
}
// count is 0,name is taonce
// count is 1,name is taonce
repeat
函数不仅仅用于集合中,它的适用范围是任意对象,需要传入两个参数:times: Int, action: (Int) -> Unit
,times
是循环的次数,action
就是闭包。通过上面的例子就能知道,闭包的执行次数有times
确定。
第五类:顶层函数
with(user) {
this.name = "kotlin"
println("user name is ${user.name}")
}
// user name is kotlin
with
比较特殊,它不是通过对象调用使用的,而是把对象作为参数传入的。适用场景,对同一个对象进行相同的操作。比如一个TextView
和一个Button
都需要显示文字内容为开始,就可以用with
同步操作:
with(view) {
this.text = "开始"
}
只需要这么写就可以完全同意处理了。
with
也是有返回值的,它和let
、run
很像,最大的不同在于with
的参数,可以对同一类型的对象进行相同的操作,比如上面对TextView
和Button
的操作,如果换做是let
和run
就不行了,因为它们是通过对象调用的方法。
写在最后
每个人不是天生就强大,你若不努力,如何证明自己,加油!
Thank You!
--Taonce
如果你觉得这篇文章对你有所帮助,那么就动动小手指,扫描下方的二维码,关注一波吧~~非常期待大家的加入
专注Kotlin知识的公众号
网友评论