Kotlin基础篇(二)-作用域函数
作用域函数存在于Standard.kt中。
主要作用是在一个对象上执行代码块,并提供lambda表达式,从而生成一个临时作用域,而在这个作用域中,我们可以直接访问该对象。
standard文件不大,方法也不算太多,我们都具体的看下:
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")
在standard.kt中定义了两个todo函数,也就是我们之前在java中使用的注解todo,当我们使用todo函数时,总是会抛出NotImplementedError,从而提示我们当前方法未实现。
run()
run方法有两个,我们先看第一个:
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
老规矩先看源码,
首先standard中的方法都是inline修饰的,也就是内联函数。
接着run方法 传入了参数为R的block函数。
接着我们看下callsInPlace方法。
@ContractsDsl public fun <R> callsInPlace(lambda: Function<R>, kind: InvocationKind = InvocationKind.UNKNOWN): CallsInPlace
该方法定义传入lambda,并执行,第二个默认参数为InvocationKind.UNKNOWN,代表的lambda执行次数。
InvocationKind是个枚举类,其中有四个值。
public enum class InvocationKind {
//至多调用一次
@ContractsDsl AT_MOST_ONCE,
//至少调用一次
@ContractsDsl AT_LEAST_ONCE,
//调用一次
@ContractsDsl EXACTLY_ONCE,
//不知道调用几次
@ContractsDsl UNKNOWN
}
run方法最后返回bolock的返回值。
最后我们来看下怎么用:
fun main() {
print(stringBuilder())
}
fun stringBuilder(): String {
val stringBuilder = StringBuilder()
stringBuilder.append("1")
stringBuilder.append("2")
stringBuilder.append("3")
return stringBuilder.toString()
}
上面我们定义了一个方法,返回String。
我们知道run可以传入对象,并且可以直接调用它。那么其实就是省略了
stringBuilder.append,从而直接使用append。
接着我们看run方法的第二个方法:
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
同第一个run的区别就是传入了T扩展函数,也就是当前上下文。
val s = StringBuilder().run {
append("1")
append("2")
append("3")
toString()
}
print(s)
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()
}
with有两个参数,receiver对象,以及block函数并返回block执行结果。
同样的上面stringbuilder的代码可以改为::
val sss = with(StringBuilder()) {
append("1")
append("2")
append("3")
toString()
}
println(sss)
apply
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
apply与run和with的区别就是以this为传入值,并且返回的是this,而不再是this的扩展函数block的执行结果,
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
}
alse函数则跟apply相似,都是指定传入this,调用block,最后在返回this
,区别就是block不再是this的函数,而是一个普通的函数,只是用对象this来作为参数。
val n = StringBuilder().also {
//这里就不能直接使用append了。
it.append("1")
it.append("2")
it.append("3")
}
let
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
整体来看let与also类似,区别就是let返回值是block执行结果,而also则是返回this。
val a = StringBuilder().let {
it.append("1")
it.append("2")
it.append("3")
}
在看另一种情况:
val sb: StringBuilder? = null
sb?.let {
it.append("xxx")
}
println(sb)
这时候上面的代码就类似与判空操作,与下面的代码相同:
sb?.append("xxx")
总结:
根据返回值来说:
1:run,with,let返回对象的lambda表达式执行结果
2:also,apply返回上下文对象
另外standard中除了上述五个作用域函数外,还有几个其它的方法,
我们再看下:
@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
}
@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
}
可以看到takeIf和takeUnless其实差不多,区别就是predicate(this)的值是否为true。
val num = Random.nextInt(100)
val takeIf = num.takeIf { it % 2 == 0 }
val takeUnless = num.takeUnless { it % 2 == 0 }
println("$num ----- $takeIf ---$takeUnless")
如上面所示,我们随机100以内的int值,打印结果如下:
另一点就是,由于takeIf和takeUnless放回值可为空,所以我们如果链式调用其方法时,需要判空。
repeat函数是Standard.kt中的最后一个函数了,
@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
contract { callsInPlace(action) }
for (index in 0 until times) {
action(index)
}
}
repeat函数两个参数,一个Int类型的times,另一个action。
另外代码中for循环很简单,根据times的值从0循环执行action。
使用起来很简单,:
repeat(100) {
println("这是repeat的$it")
}
如上,我们传入循环次数100,打印结果从0-99.