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)
}