Kotlin中的那些事之——作用域函数。run,with,let,also,apply

前言

熟悉kotlin的同学都知道,kotlin中有一些很好用的函数。比如runwithletalsoapply。我们平时写代码的过程中肯定都有在使用这些函数,但是我们使用的过程中有留意这些函数是怎么实现的吗?或者说我们有平时有去了解过这些函数的区别吗?如果没有了解过,那么不要紧,这篇文章带你去了解一下kotlin中的这几个基本的函数。在讲解这几个函数之前,我们首先去了解一个概念——作用域函数

作用域函数

在Kotlin有这么一类函数,它们的唯一目的是在对象的上下文中执行代码块当对一个对象调用这样的函数并提供一个lambda 表达式时,它会形成一个临时作用域。在此作用域中,可以访问该对象而无需其名称。这些函数称为作用域函数。共有以下五种:letrunwithapply 以及 also

这些作用域函数的区别主要有以下三部分:

  • 是拓展函数,还是普通函数
  • 上下文对象是this还是it
  • 返回值是自身还是lambda的最后一行
      讲完了什么是作用域函数,那么我们下面就开始通过源码就解析这一类函数。
run函数
/**
 * 首先是run函数
 */
fun learnRun(){
    val name = "write code"
    val name2 = run {
        val name = "read code"
        println(name) //输出read code
        name
    }
    println(name) //输出write code
    println(name==name2) //输出true
}

//run函数的源码
//run函数的参数:接收一个函数类型的参数,这个函数的类型->是一个无参函数并且返回一个类型为R的函数
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

在这个例子中,在learnRun函数的内部有一个分隔开的作用域,在这个作用域内部完全包含一个在输出之前的name 变量被重新定义并初始化为 read code的操作实现。

这个作用域函数本身似乎看起来不是很有用。但是需要注意的一点是:它的返回值,是这个作用域内部的最后一个对象

run函数的特性:无接受者的非扩展函数,返回值为lambda表达式的结果。

with函数

普通函数实际是一个简写封装,适用于调用同一个类的多个方法或者属性场景,这时可以省去类名重复,直接调用类的方法即可。典型使用场景如下:

fun learnWith(){
    val person = Person("hui","boy")
    with(person){
        print(name)
        print(sex)
    }
}
//with函数的源码
 //特别注意!block 参数是一个带接收者的函数类型,T.()->R 里的 this 代表的是自身实例,所以可以省
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}
 

注意:Kotlin 提供了调用带有接收者(提供接收者对象)的函数类型实例的能力,如上面的T.() -> R。在这样的函数字面值内部,传给调用的接收者对象成为隐式的this,以便访问接收者对象的成员而无需任何额外的限定符,亦可使用 this 表达式访问接收者对象。

with函数的特性:一个非扩展函数,上下文对象作为lambda表达式的接受者,但是在 lambda 表达式内部,它可以作为接收者(this)使用。 返回值是 lambda 表达式结果。

T.let函数

对于 T.let 函数的声明,你将会注意到 T.let 是传递它自己本身到函数中block: (T) 。将上下文对象作为 lambda 表达式参数。如果没有指定参数名,对象可以用隐式默认名称 it 访问。itthis 简短,带有 it 的表达式通常更容易阅读。然而,当调用对象函数或属性时,不能像 this 这样隐式地访问对象。必须使用it.这种调用方式去访问对应的属性。

fun learnLet(){
    var person:Person? = Person("hui","boy")
    person?.let {
        print(it.name)
        print(it.sex)
    }
}
//T.let源码
 //let 是一个 inline 内联扩展函数,扩展任意类型,此处范型表示
 //接收一个函数类型参数,函数的参数是这个对象,并且返回一个类型为R的函数
 @kotlin.internal.InlineOnly
 public inline fun <T, R> T.let(block: (T) -> R): R {
     //契约
     contract {
         callsInPlace(block, InvocationKind.EXACTLY_ONCE)
     }
     //可以看到调用了函数参数的函数,传递参数为调用 let 函数的对象实例,返回值为函数参数调用返回值
     return block(this)
 }

T.let函数的特性:一个扩展函数,将上下文对象作为 lambda 表达式参数,没有声明lambda参数的时候可以使用it指代上下文对象返回值是 lambda 表达式结果。

T.run函数
fun learnTRun(){
    var person:Person? = Person("hui","boy")
    person?.run {
        print(name)
        print(sex)
    }
}
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

T.run函数与T.let函数非常的像,他们只是参数不同,一个是it类型,一个是this类型

T.run函数的特性:一个扩展函数,将上下文对象作为 lambda 表达式的接受者,在lambda表达式内部,他它可以作为接受者(this)使用。 返回值是 lambda 表达式结果。

T.also函数

T.also 扩展函数和 T.let 很像,唯一的区别就是返回值不一样,T.let 是以闭包的形式返回函数体内最后一行的值,而 also 扩展函数返回的是传入对象的自身。

 public inline fun <T> T.also(block: (T) -> Unit): T {
     contract {
         callsInPlace(block, InvocationKind.EXACTLY_ONCE)
     }
     block(this)
     //返回调用对象自己
     return this
 }

T.also函数的特性:一个扩展函数,将上下文对象作为 lambda 表达式参数,没有声明lambda参数的时候可以使用it指代上下文对象返回值上下文对象。

T.apply函数

T.apply 扩展函数和 T.run 很像,唯一的区别就是返回值不一样,T.dun 是以闭包的形式返回函数体内最后一行的值,而 T.apply 扩展函数返回的是传入对象的自身。

@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

T.apply函数的特性:一个扩展函数,将上下文对象作为 lambda表达式的接受者,但是在 lambda 表达式内部,它可以作为接收者(this)使用。 返回值上下文对象。

如何选择哪一种参数this还是it

选择this的场景:当你访问接收者对象时你可以省略 this,来让你的代码更简短。相对地,如果省略了 this,就很难区分接收者对象的成员及外部对象或函数。因此,对于主要对对象成员进行操作(调用其函数或赋值其属性)的 lambda 表达式,建议将上下文对象作为接收者(this

选择it的场景:当上下文对象在作用域中主要用作函数调用中的参数时,使用 it 作为上下文对象会更好。若在代码块中使用多个变量,则 it 也更好。

总结

函数类型参数类型(it,this)返回值(本身,最后一行)
run非拓展函数it最后一行
with非拓展函数this最后一行
T.let拓展函数it最后一行
T.run拓展函数this最后一行
T.also拓展函数it本身
T,apply拓展函数this本身
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值