Kotlin学习笔记30 补充 作用域函数

参考链接

https://www.kotlincn.net/docs/reference/scope-functions.html

背景介绍

1.本节主要讲述几个Kotlin库提供的几个方便的方法

let、run、with、apply 以及 also

takeIf以及 takeUnless

2.let、run、with、apply 以及 also的主要作用是在对象的上下文中执行代码块

3.当对一个对象调用这样的函数并提供一个 lambda 表达式时,它会形成一个临时作用域。在此作用域中,可以访问该对象而无需其名称。这些函数称为作用域函数

该作用域可以理解为花括号之内的区域

4.let、run、with、apply 以及 also的主要区别在于他们在作用域中如何引用自身以及他们作用域的返回值

5.takeIf 及 takeUnless的主要作用是检查对象状态

apply函数详解

/**
 * apply函数详解
 *
 * 1 apply函数返回调用者本身
 * 2 apply内部可以使用this代表调用者
 *
 * apply可以进行链式调用
 */

fun main() {
    val info = "Cai Huijian"
    // 不使用apply方法
    println("apply 函数里面打印的this是 $info")
    println("info 长度是${info.length}")
    println("info 最后一个字符是${info[info.length - 1]}")
    println("info 全部转为小写:${info.lowercase(Locale.getDefault())}")

    println("==============")

    // 使用apply方法
    val info2 = info.apply {
        println("apply 函数里面打印的this是 $this")
        println("info 长度是${/*this.*/length}") // this 可以省略
        println("info 最后一个字符是${this[/*this.*/length - 1]}")
        println("info 全部转为小写:${/*this.*/lowercase(Locale.getDefault())}")
    }
    println(info2)// apply方法返回调用者自身 这里是info
    println("==============")



    // 链式调用vs普通调用
    // 普通调用
    val file = File("D:\\a.txt")
    file.setExecutable(true)
    file.setReadable(true)
    println(file.readLines())

    // 由于apply返回调用者自身 因此可以进行链式调用
    println("==============")
    file.apply {
        setExecutable(true)
    }.apply {
        setReadable(true)
    }.apply {
        println(file.readLines())
    }

    // 也可以这么写
    file.apply {
        this.setExecutable(true)
        this.setReadable(true)
        println(this.readLines())
    }

    // 另外也可以这么写
    file.apply {
        file.setExecutable(true)
        file.setReadable(true)
        println(file.readLines())
    }
}

let函数详解

/**
 * let函数详解
 *
 * 1 let函数返回的是lambda表达式 即根据lambda表达式最后一行不同而不同
 * 2 let内部可以使用it代表调用者
 *
 * let的应用:与?结合用于判空
 * 安全调用运算符 ?. 以及 Elvis运算符(空值合并运算符)  ?:
 */

fun main() {
    // 普通方式 对集合第一个元素相加
    val list = listOf(6, 5, 4, 7, 8)
    val value1 = list.first()
    val res = value1 + value1
    println(res)

    // 使用let 对集合第一个元素相加
    val res2 = list.let {
        it.first() + it.first()// res2的值类型为let表达式的最后一行
        //true
        //"abc"
    }
    println(res2)
    println("===============")

    // 普通方式判空
    println(getMethod1(/*null*/"chj"))
    println(getMethod2(/*null*/"chj"))
    println(getMethod3(/*null*/"chj"))
    println(getMethod4(/*null*/"chj"))
}

// 普通方式判空
fun getMethod1(value: String?): String {
    return if (value == null) "传入空参" else "参数不为空 $value"
}

// 普通方式判空 简化版
// 这里用的是单表达式函数 当函数返回单个表达式时,可以省略花括号并且在 = 符号之后指定代码体即可
fun getMethod2(value: String?) = if (value == null) "传入空参" else "参数不为空 $value"

// let方式判空 let结合? 可以方便地判空
// 安全调用运算符?. 用于可空类型变量安全调用非空类型的属性或者函数,而不会抛出空指针异常,当变量为null时,直接返回空值null,否则进行调用并返回结果
// Elvis运算符(空值合并运算符):  ?:    ?:是一个二元运算符,作用是判断可空类型时空值合并,语法为:可空类型数据 ?: 空值合并到的数据,
// Kotlin 中不存在三目运算符。当数据非空时,直接返回数据,当数据为空时,返回合并到的数据
fun getMethod3(value: String?): String {
    return value?.let {
        "参数不为空 $it"// 走到这里 value必定不为空
    } ?: "传入空参"
}

// let方式判空 let结合? 简化版
fun getMethod4(value: String?) =
    value?.let {
        "参数不为空 $it"// 走到这里 value必定不为空
    } ?: "传入空参"

run 函数详解

/**
 * run 函数详解
 *
 * 1 let函数返回的是lambda表达式 即根据lambda表达式最后一行不同而不同
 * 2 let内部可以使用this代表调用者
 *
 * 3 使用lambda表达式链式调用
 */

fun main() {
    val str = "Huijian Cai"
    val res1: Float = str.run {
        println(this)// let内部可以使用this代表调用者
        true
        3.14f // 返回值是最后一行
    }
    println(res1)

    // 使用具名函数进行链式调用
    str.run(::isLong)//返回Boolean
        .run(::printText)// 根据Boolean返回不同text
        .run(::mapText)// 对text做处理
        .run(::println)// 输出text
    println("===========")

    // 使用匿名函数进行链式调用
    str.run { length > 5 }
        .run { if (this) "字符串合格" else "字符串太短" }
        .run { "[$this]" }
        .run { println(this) }
}

fun isLong(string: String) = string.length > 5

fun printText(isLong: Boolean) = if (isLong) "字符串合格" else "字符串太短"

fun mapText(getShow: String) = "[$getShow]"

with 函数详解

/**
 * with 函数详解
 *
 * 1 with函数返回的是lambda表达式 即根据lambda表达式最后一行不同而不同
 * 2 with内部可以使用this代表调用者
 *
 * 总结 with 和 let 几乎完全一样 只不过调用方式有些许差别
 * with(str){
 * }
 *
 * str.let{
 * }
 */

fun main() {
    val string = "abc"

    // 具名函数操作
    val r1 = with(string, ::getStrLength)
    val r2 = with(r1, ::getLengthInfo)
    val r3 = with(r2, ::getInfoMap)
    with(r3, ::show)

    // 匿名操作
    with(with(with(with(string) {
        this.length
    }) {
        "你的字符串长度为:$this"
    }) {
        "[$this]"
    }) {
        println(this)
    }
}

fun getStrLength(string: String) = string.length
fun getLengthInfo(length: Int) = "你的字符串长度为:$length"
fun getInfoMap(info: String) = "[$info]"
fun show(content: String) = println(content)

also 函数详解

/**
 * also 函数详解
 *
 * 1 also 函数返回的是调用者本身
 * 2 also 内部可以使用it代表调用者
 *
 */
fun main() {
    val str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

    val r1 : String = str.also {// r1 始终是str本身 不会管最后一行
        true
        3434.4f
        454
        'C'
        1
    }
    println(r1)
    println("=============")

    str.also {
        println(it)// 在内部 可以使用it代表调用者本身
    }

    // 由于also始终返回调用者本身 可以进行链式调用
    str.also {
        println("原始数据1:$str")
        println("原始数据2:$it")
    }.also {
        println("转换为小写${it.lowercase(Locale.getDefault())}")
    }.also {
        println("end")
    }
    println(str)// 不管如何操作 原始数据不变
    println("==============")

    // 链式调用的优势

    // 普通调用
    val file = File("D:\\a.txt")
    file.setExecutable(true)
    file.setReadable(true)
    println(file.readLines())
    println()
    // also链式调用
    val fileNew:File = file.also {
        file.setExecutable(true)// 可以使用file或者it代表外部变量
        it.setReadable(true)
    } .also {
        println(file.readLines())// 也可以拆成多次also调用
    }
    println(fileNew === file)
}

takeIf 内置函数

/**
 * takeIf 内置函数
 * takeIf 简单使用
 *
 * name.takeIf {true/false}
 * true: 返回name本身
 * false: 返回null
 */

fun main() {
    // 调用简单的takeIf
    val result = checkPermission("hjcai", "123")
    if (result != null) {
        println("欢迎 $result 登录成功")
    }else{
        println("登录失败")
    }
    println("================")

    // 调用 takeIf 与空合并操作符?:结合
    println(checkPermission2("hjcai","123"))
    // 真实情况下 takeIf经常和空合并操作符?: 联合使用
}

private fun checkPermission(name: String, pwd: String): String? {
    return name.takeIf {
        permissionSystem(name, pwd)
    }
}

/**
 * takeIf 与空合并操作符?:结合
 *
 * 如果takeIf内部为true 返回name 否则返回字符串"登录失败"
 */
private fun checkPermission2(name: String, pwd: String): String {
    return name.takeIf {
        permissionSystem(name, pwd)
    } ?: "登录失败"
}

private fun permissionSystem(userName: String, userPwd: String): Boolean {
    return userName == "hjcai" && userPwd == "123"
}

takeUnless 内置函数

/**
 * takeUnless 内置函数
 * takeUnless与takeIf 完全相反
 *
 * name.takeUnless {true/false}
 * true: 返回null
 * false: 返回name本身
 *
 * name.takeIf {true/false}
 * true: 返回name本身
 * false: 返回null
 *
 * 为什么需要takeUnless? takeIf一个就够了?
 * takeUnless的目的是 结合isNullOrBlank()判断变量是否经过初始化
 */

class Manager{
    var infoValue:String? = null
}

fun main() {
    val manager = Manager()
    val r = manager.infoValue.takeUnless { it.isNullOrBlank() } ?:"没有初始化"
    println(r)

    manager.infoValue = "初始化为aaa"
    val r2 = manager.infoValue.takeUnless { it.isNullOrBlank() } ?:"没有初始化"
    println(r2)
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Kotlin协程的作用域函数有五种:let、run、with、apply以及also。这些函数基本上做了同样的事情:在一个对象上执行一个代码块。例如,我们可以使用apply函数来初始化一个对象的属性,如下所示:val adam = Person("Adam").apply { age = 20 city = ... }。通过apply函数,我们可以在代码块内直接访问和修改对象的属性,从而简化了对象的初始化过程。 在使用协程的过程中,如果我们想要判断协程是否被取消了,根据不同的情况采取相应的处理逻辑,可以使用if语句和isActive属性来判断。但是这样做可能比较繁琐且容易出错。另一种更简洁的方式是使用yield函数,yield函数是官方协程框架提供的一个对逻辑没有影响的挂起函数。通过使用yield函数,我们可以在协程中暂停执行,并且不需要关心协程的取消状态。 除了官方提供的几种作用域函数外,我们还可以通过继承或组合的方式创建自定义的协程作用域。例如,在ViewModel或Service中,我们可以使用SupervisorJob来创建自己的CoroutineScope对象。通过自定义的协程作用域,我们可以管理和控制协程的生命周期,并且可以处理协程中可能发生的异常。比如,在ViewModel中,我们可以在onCleared方法中取消自定义的协程作用域,确保协程的正确关闭。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Kotlin作用域函数之间的区别和使用场景详解](https://download.csdn.net/download/weixin_38690830/14014853)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【深入理解Kotlin协程】协程作用域、启动模式、调度器、异常和取消【使用篇】](https://blog.csdn.net/lyabc123456/article/details/127800121)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值