欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Kotlin基础篇(二)-作用域函数

程序员文章站 2024-03-15 09:00:59
...

作用域函数存在于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)

Kotlin基础篇(二)-作用域函数
这时候上面的代码就类似与判空操作,与下面的代码相同:

 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值,打印结果如下:
Kotlin基础篇(二)-作用域函数
另一点就是,由于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.

相关标签: kotlin