• Kotlin学习笔记(六)作用域函数run,with, apply,also,let,takeIf,takeUnless,repeat


    作用域函数,就是些技巧性方法,函数定义全在Standard.kt文件里。本篇,用实例记录一下它们的用法。

    1.所谓作用域函数:它们的lamda函数作用域===对象类内部作用域。 就像是对象类的内部方法一样。

    这样在lamda函数内,可以直接调用对象的方法,属性,不需要通过对象实例点出方法,属性。

    2. let/takeIf/takeLess, 需要it引导

    3. with是独立函数,其他函数都可由对象点出来。

    • run,利用操作对象的方法属性,完成一些工作。最后在函数体内返回指定值。
    1. val list = listOf("apple", "orange", "pear")
    2. val result = StringBuilder().run {
    3. append("Start eating fruits.\n")
    4. for (fruit in list){
    5. append(fruit).append("\n")
    6. }
    7. append("Ate all fruits.")
    8. toString()
    9. }
    10. println(result)

    • apply,用于修改对象自己。修改后,如果函数有接收返回,接收的也是修改后的自己。函数体类不需要返回对象。
    1. fun main() {
    2. val user = User("alan", 18)
    3. val applyResult = user.apply {
    4. //这儿都是user对象的作用域
    5. age = 20
    6. }
    7. println(applyResult) //输出:User(name=Peter, age=20)
    8. println(user) //输出:User(name=Peter, age=20)
    9. println(applyResult === user) //输出:true
    10. }
    11. data class User(var name: String, var age: Int)

    第二个例子:

    1. val list = listOf("apple", "orange", "pear")
    2. val result = StringBuilder().apply{
    3. append("Start eating fruits.\n")
    4. for (fruit in list){
    5. append(fruit).append("\n")
    6. }
    7. append("Ate all fruits.")
    8. }
    9. println(result.toString())
    • with,作用和run一样。不同的是,run由对象点出,而with是独立的函数,对象是作为with的参数。
    1. val list = listOf("apple", "orange", "pear")
    2. val result = with(StringBuilder()) {
    3. append("Start eating fruits.\n")
    4. for (fruit in list){
    5. append(fruit).append("\n")
    6. }
    7. append("Ate all fruits.")
    8. toString()
    9. }
    10. println(result)

    • let, 把原始对象作为lamda表达式的参数,传入函数体。通常用来简化判空操作, 而且是线程安全的。
    1. object.let{
    2. it.todo()//在函数体内使用it替代object对象去访问其公有的属性和方法
    3. ...
    4. }
    5. //另一种用途 判断object为null的操作
    6. object?.let{//表示object不为null的条件下,才会去执行let函数体
    7. it.todo()
    8. }

     下面的写法,有点罗嗦,如果使用user的参数方法更多,就要被问号烦死了。

    1. fun doStudy(user:User?){
    2. user?.age = 20
    3. user?.name = "alan"
    4. }

    或者用if判空:

    1. fun doStudy(user:User?){
    2. if(user != null){
    3. user.age = 20
    4. user.name = "alan"
    5. }
    6. }

    用let函数简化:

    1. fun doStudy(user:User?){
    2. user?.let{
    3. it.age = 20
    4. it.name = "alan"
    5. }

    针对if判空那种方式,是有问题的。当User对象是全局对象的时候。就编译错误了。提示有多线程问题。

    1. var user:User? = null
    2. fun doStudy() {
    3. if (user!=null) {
    4. user.age = 20 //编译错误提醒
    5. user.name = "alan"//编译错误提醒
    6. }
    7. }
    • also, 和let的作用一样。不同点就是,let函数的返回值是最后一行的返回值,而also函数的返回值是返回上下文对象本身。可以理解为"并且为该对象执行以下操作"。

    一般可用于多个扩展函数链式调用。

    1. fun main() {
    2. val user = User("alan", 18)
    3. val alsoResult = user.also {
    4. println("user 之前的名字是:${it.name}") //user 之前的名字是:alan
    5. }.apply {
    6. name = "lin"
    7. }
    8. println(alsoResult) //User(name=lin, age=18)
    9. println(user) //User(name=lin, age=18)
    10. println(alsoResult === user) //true
    11. }
    12. data class User(var name: String, var age: Int)
    • takeIf,takeunless, 前面调用了一个函数计算得出了一个结果,现在需要对这个结果做一个分支判断,并且我们只需要用到if的一个分支时,可以用takeIf和takeUnless代替。

    takeIf,takeunless, 区别就只有一个是满足代码块里面的条件才返回对象本身,一个是不满足条件才返回。

    下面三段代码都是等价的:

    用if语句:

    1. fun testWithoutTakeIf() {
    2. val name = "alan Gong"
    3. val hasGong = name.indexOf("Gong")
    4. Log.i(TAG, "testWithoutTakeIf: hasGong = $hasGong")
    5. if (hasGong >= 0) {
    6. Log.i(TAG, "testWithoutTakeIf: has Gong")
    7. }
    8. Log.i(TAG, "testWithoutTakeIf: $name")
    9. }
    10. 输出:
    11. I: testWithoutTakeIf: hasGong = 0
    12. I: testWithoutTakeIf: has Gong
    13. I: testWithoutTakeIf: alan Gong

    用takeIf:

    1. fun testTakeIf() {
    2. val name = "alan Gong"
    3. name.indexOf("Gong")
    4. .takeIf {
    5. Log.i(TAG, "testTakeIf: it = $it")
    6. it >= 0
    7. }
    8. ?.let {
    9. Log.i(TAG, "testTakeIf: has Gong")
    10. }
    11. Log.i(TAG, "testTakeIf: $name")
    12. }
    13. 输出:
    14. I: testTakeIf: it = 0
    15. I: testTakeIf: has Gong
    16. I: testTakeIf: alan Gong

    用takeLess: 注意函数体的判断和takeIf函数体里相反。

    1. fun testTakeUnless() {
    2. val name = "alan Gong"
    3. name.indexOf("Gong")
    4. .takeUnless {
    5. Log.i(TAG, "testTakeUnless: it = $it")
    6. it < 0
    7. }
    8. ?.let {
    9. Log.i(TAG, "testTakeUnless: has Gong")
    10. }
    11. Log.i(TAG, "testTakeUnless: $name")
    12. }
    13. 输出:
    14. I: testTakeUnless: it = 0
    15. I: testTakeUnless: has Gong
    16. I: testTakeUnless: alan Gong

    由上面的简单例子也可以看出,这两个通常和其他作用域函数,组成链式调用。

    • repeat, 循环执行n次block中的代码。
    1. repeat(3){
    2. println("repeat")
    3. }

    好了,目前所有的作用域函数都记录清楚了。看起来是挺强大的,但是这种东西多了,可读性就不见得好了。另外还有一张网络上的总结图:

  • 相关阅读:
    Moderate Modular Mode(构造,数论,1600)
    高频前端面试题汇总之HTML篇
    渗透测试-渗透测试常见的总结
    【寒枫顾辞老航小说传】第一回:梦回大唐
    java-php-python-springboot校园博客系统计算机毕业设计
    MySQL高级
    为何基于树的模型在表格型数据中能优于深度学习?
    [附源码]计算机毕业设计JAVAjsp建筑公司门户网站
    iOS 16.2 的7个惊人变化
    解决flex-direction: column 之后元素宽度自动变为100%
  • 原文地址:https://blog.csdn.net/zjuter/article/details/126139511