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.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值