kotlin 作用域函数 : let、run、with、apply、 also、takeIf、takeUnless
1.官方文档
英文: https://kotlinlang.org/docs/reference/scope-functions.html
中文: https://www.kotlincn.net/docs/reference/scope-functions.html
2.简介
- 在kotlin标准库的Standard.kt 文件中,定义了一系列函数模板。其中的 [ run 、with、let、apply、aloso 、takeIf、takeUnless] 称作用域函数。
- 它们有个共同点是,最后一个参数都是一个函数指针,当使用 lambda 表达式 方式调用这些函数时,在{ } 内部,可以访问调用者对象而无需其名称,所以叫它们作用域函数。
- 它们的作用就是让对象上执行一个额外代码,在某些情况下,可以简化代码。
- 它们生成对象类型的扩展函数(with除外)
部分代码定义如下:
1 public inline fun <T> T.apply(block: T.() -> Unit): T { 2 contract { 3 callsInPlace(block, InvocationKind.EXACTLY_ONCE) 4 } 5 block() 6 return this 7 } 8 9 /** 10 * Calls the specified function [block] with `this` value as its argument and returns `this` value. 11 * 12 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#also). 13 */ 14 @kotlin.internal.InlineOnly 15 @SinceKotlin("1.1") 16 public inline fun <T> T.also(block: (T) -> Unit): T { 17 contract { 18 callsInPlace(block, InvocationKind.EXACTLY_ONCE) 19 } 20 block(this) 21 return this 22 } 23 24 /** 25 * Calls the specified function [block] with `this` value as its argument and returns its result. 26 * 27 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#let). 28 */ 29 @kotlin.internal.InlineOnly 30 public inline fun <T, R> T.let(block: (T) -> R): R { 31 contract { 32 callsInPlace(block, InvocationKind.EXACTLY_ONCE) 33 } 34 return block(this) 35 }
3.内置的几个函数对照表
3.1 对比表
简称 | 原型 | 作用 | 返回值类型 | 对象的引用名 |
run |
inline fun <R> run(block: () -> R): R |
|
代码块的返回类型 | - |
inline fun <T, R> T.run(block: T.() -> R): R |
对象配置并且计算结果 |
代码块的返回类型 | this | |
with |
inline fun <T, R> with(receiver: T, block: T.() -> R): R |
以对象为参数执行一段代码 |
代码块的返回类型 | this |
let |
inline fun <T, R> T.let(block: (T) -> R): R |
|
代码块的返回类型 | it |
also |
inline fun <T> T.also(block: (T) -> Unit): T |
给对象添加附加效果 |
对象类型 | it |
apply |
inline fun <T> T.apply(block: T.() -> Unit): T |
对象配置 |
对象类型 | this |
takeIf |
inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? |
predicate(对象) 执行结果为true返回对象,相反null | 对象类型 或 null | it |
takeUnless |
inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? |
!predicate(对象) 执行结果为fase返回对象,相反null | 对象类型 或 null | it |
- also,apply,takeIf,takeUnless , a和 t 开头的返回对象类型,其它的返回代码块类型。
- also,let , takeIf,takeUnless 引用对象用的是it,其它用this
3.2 注意事项
适当的场景下,使用作用域函数可以使代码变简洁,但请:
- 避免过度使用: 这会降低代码的可读性并可能导致错误。
- 避免嵌套使用
- 避免链式调用:此时很容易对当前上下文对象及
this
或it
的值感到困惑。
4.let
4.1 作用
1 /** 2 * Calls the specified function [block] with `this` value as its argument and returns its result. 3 * 4 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#let). 5 */ 6 @kotlin.internal.InlineOnly 7 public inline fun <T, R> T.let(block: (T) -> R): R { 8 contract { 9 callsInPlace(block, InvocationKind.EXACTLY_ONCE) 10 } 11 return block(this) 12 }
- 对一个非空(non-null)对象执行 lambda 表达式
- 将表达式作为变量引入为局部作用域中
4.2 测试
- 最后一行是返回值
1 class Scope2(var name : String) 2 3 //1.最后一行是返回值 4 val num = 12.let { 5 3 6 Log.e(ScopeTAG, "testLet it = $it") 7 } 8 Log.e(ScopeTAG, "testLet num = $num")
结果:
testLet it = 12 testLet num = 31
- 函数式调用
1 //2.函数式调用 2 fun <T> leeeeeeeeeeeet1(it : T){ 3 Log.e(ScopeTAG,"testLet let1<T> it = $it") 4 } 5 13f.let(::leeeeeeeeeeeet1)
结果
testLet let1<T> it = 13.0
- 非空值调用 let
1 //3.非空值调用 let 2 val flt = 13f.let { 16 } 3 Log.e(ScopeTAG, "testLet flt = $flt")
结果
testLet flt = 16
- 非空对象调用
1 //4.非空对象调用 2 val fullName = Scope2("not-").let { 3 val last = "null" 4 it.name += last 5 Log.e(ScopeTAG, "testLet name append $last ") 6 it.name 7 } 8 Log.e(ScopeTAG, "testLet fullName = $fullName ")
结果
testLet name append null testLet fullName = not-null
- 空对象调用
1 //5.空对象调用 2 val sc2 : Scope2 ? = null 3 val name2 = sc2 ?. let { 4 it.name += "fff" //虽然sc2 为 null ,但是由于是sc2 ?. 调用,所以在{}内不用以 ?.方式使用it 5 } 6 Log.e(ScopeTAG, "testLet name2 = $name2 ")
结果
testLet name2 = null
- 将表达式作为变量引入为局部作用域中
1 //6.将表达式作为变量引入为局部作用域中 2 1 + (2 * 300) .let { 3 Log.e(ScopeTAG, "testLet 1 + (2 * 300) = $it ") 4 }
结果
testLet 1 + (2 * 300) = 600
- 静态类或者伴生对象.let,访问静态成员
1 //7.静态类或者伴生对象.let,访问静态成员 2 Int.let { 3 //对Int的伴生对象调用let 4 Log.e(ScopeTAG, "testLet Int.MIN_VALUE = ${it.MIN_VALUE} , SIZE_BYTES = ${it.SIZE_BYTES} , it = $it") 5 }
结果
testLet Int.MIN_VALUE = -2147483648 , SIZE_BYTES = 4 , it = kotlin.jvm.internal.IntCompanionObject@e99cd85
5.apply
5.1 作用
1 /** 2 * Calls the specified function [block] with `this` value as its receiver and returns `this` value. 3 * 4 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#apply). 5 */ 6 @kotlin.internal.InlineOnly 7 public inline fun <T> T.apply(block: T.() -> Unit): T { 8 contract { 9 callsInPlace(block, InvocationKind.EXACTLY_ONCE) 10 } 11 block() 12 return this 13 }
主要用来对象配置
5.2 测试
- 配置对象
1 class Scope3 { 2 var name = "" 3 var age = 0 4 var first = "" 5 var last = "" 6 var address = "" 7 var phone = "" 8 9 10 override fun toString(): String { 11 return "name = $name,age = $age,first = $first,last = $last,address = $address,phone = $phone" 12 } 13 } 14 //1.主要用来配置对象 15 val sc3 = Scope3().apply { 16 name = "eot" 17 age = 12 18 first = "li" 19 last = "per" 20 address = "ooeoeooe" 21 phone = "13xxxxxxx" 22 } 23 24 Log.e("scope_test","testApply sc3 : $sc3")
结果
testApply sc3 : name = eot,age = 12,first = li,last = per,address = ooeoeooe,phone = 13xxxxxxx
- 静态类或有内部静态类可以使用类名.apply{} ,没有静态内部类的不支持
1 class Apply{ 2 companion object{ 3 var value = 2046 4 override fun toString(): String { 5 return "value = $value" 6 } 7 } 8 } 9 object Apply2{ 10 var value = 99 11 override fun toString(): String { return """value = $value""" } 12 } 13 class Apply3{ 14 object Static1{ 15 var value1 = 99 16 override fun toString(): String { return """value1 = $value1""" } 17 } 18 object Static2{ 19 var value2 = 99 20 override fun toString(): String { return """value2 = $value2""" } 21 } 22 } 23 24 //2.静态类或有内部静态类可以使用类名.apply{} ,没有静态内部类的不支持 25 val a0 = Apply.apply { value = 11 } 26 Log.e(ScopeTAG,"testApply a0 : $a0") 27 28 val a4 = Apply2.apply { value = 55 } 29 Log.e(ScopeTAG,"testApply s4 : $a4") 30 31 val a3 = Apply3.Static1.apply { value1 = 66 } 32 Log.e(ScopeTAG,"testApply a3 : $a3")
结果
testApply a0 : value = 11 testApply s4 : value = 55 testApply a3 : value1 = 66
- kotlin内置常用类都有静态内部类
1 val a1 = Int.apply { 2 123 3 12 * 23 4 } 5 val a2 = String.apply { """hello"""} 6 Log.e(ScopeTAG,"testApply a1 : $a1 ,a2 : $a2")
结果
testApply a1 : kotlin.jvm.internal.IntCompanionObject@6a122e8 ,a2 : kotlin.jvm.internal.StringCompanionObject@2aebd01
- 没有静态内部类的不支持,编译不过
1 //3.没有静态内部类的不支持 2 class Apply4 { var value = 22} 3 val a6 = Apply4.apply { value = 77 } 4 Log.e(ScopeTAG,"testApply a6 : $a6")
结果: 编译不过
6.run
6.1 作用
这个函数有两个版本
全局函数版本
1 /** 2 * Calls the specified function [block] and returns its result. 3 * 4 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#run). 5 */ 6 @kotlin.internal.InlineOnly 7 public inline fun <R> run(block: () -> R): R { 8 contract { 9 callsInPlace(block, InvocationKind.EXACTLY_ONCE) 10 } 11 return block() 12 }
扩展函数版本
1 /** 2 * Calls the specified function [block] with `this` value as its receiver and returns its result. 3 * 4 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#run). 5 */ 6 @kotlin.internal.InlineOnly 7 public inline fun <T, R> T.run(block: T.() -> R): R { 8 contract { 9 callsInPlace(block, InvocationKind.EXACTLY_ONCE) 10 } 11 return block() 12 }
全局函数版本: 运行一段代码,无参数。
扩展函数版本 : 对象配置并且计算结果
6.2 测试
- 全局函数版
1 //1.全局函数版 2 run { 3 Log.e(ScopeTAG,"testRun 全局函数版: 运行代码") 4 } 5 val i3 = 122 * 2 + run { Log.e(ScopeTAG,"testRun 全局函数版: 在表达式中运行语句"); 23 } + 34 6 Log.e(ScopeTAG,"testRun 全局函数版: i3 = $i3 (122 * 2 + 23 + 34)")
结果
testRun 全局函数版: 运行代码
testRun 全局函数版: 在表达式中运行语句
testRun 全局函数版: i3 = 301 (122 * 2 + 23 + 34) - 扩展函数版本
1 //2.扩展函数版本 2 class A(var name : String){ fun query() : String = "name = $name" } 3 4 val result = A("null").run { 5 this.name = "r1" 6 query() 7 } 8 Log.e(ScopeTAG,"testRun result = $result")
结果
testRun result = name = r1
7.with
7.1 作用
1 /** 2 * Calls the specified function [block] with the given [receiver] as its receiver and returns its result. 3 * 4 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#with). 5 */ 6 @kotlin.internal.InlineOnly 7 public inline fun <T, R> with(receiver: T, block: T.() -> R): R { 8 contract { 9 callsInPlace(block, InvocationKind.EXACTLY_ONCE) 10 } 11 return receiver.block() 12 }
以对象为参数,执行以下操作
7.2 测试
1 fun testWith(){ 2 //1.全局函数版 3 with(3) { 4 Log.e(ScopeTAG,"testWith this = $this") 5 } 6 7 //2.扩展函数版本 8 class A(var name : String) 9 10 val w1 = A("null") 11 Log.e(ScopeTAG,"testWith r1.name = ${w1.name}") 12 with(w1) { 13 this.name = "w1" 14 Log.e(ScopeTAG,"testWith name = $name") 15 } 16 17 //3.同理,对静态类或者有伴生对象的类 18 with(Int){ 19 Log.e(ScopeTAG, "testWith Int.MIN_VALUE = ${this.MIN_VALUE} , SIZE_BYTES = ${this.SIZE_BYTES} , it = $this") 20 } 21 with(Apply2){ 22 Log.e(ScopeTAG, "testWith Apply2.value = ${this.value} , this = $this") 23 } 24 25 //4.空对象 26 val nullInt : Int? = null 27 with(nullInt){ 28 Log.e(ScopeTAG, "testWith nullInt = $this") 29 } 30 31 //5.在表达式中 32 val ret = 12 + 29 * with(3){ //注意优先级 12 + (29 * 6) = 186 33 this * 2 34 } 35 Log.e(ScopeTAG, "testWith ret = $ret") 36 37 }
结果
testWith this = 3
testWith r1.name = null
testWith name = w1
testWith Int.MIN_VALUE = -2147483648 , SIZE_BYTES = 4 , it = kotlin.jvm.internal.IntCompanionObject@3222e75
testWith Apply2.value = 55 , this = value = 55
testWith nullInt = null
testWith ret = 186
8.also
8.1 作用
1 /** 2 * Calls the specified function [block] with `this` value as its argument and returns `this` value. 3 * 4 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#also). 5 */ 6 @kotlin.internal.InlineOnly 7 @SinceKotlin("1.1") 8 public inline fun <T> T.also(block: (T) -> Unit): T { 9 contract { 10 callsInPlace(block, InvocationKind.EXACTLY_ONCE) 11 } 12 block(this) 13 return this 14 }
当你在代码中看到 also
时,可以将其理解为“并且用该对象执行以下操作”。
8.2 测试
1 object Also{ var value = 1 ; override fun toString(): String { return "value = $value" } } 2 3 fun testAlso(){ 4 //1,对象并且执行的 5 val name = "testAslo-hello" 6 val sub = name.substringAfter("-").also { 7 it.toUpperCase() 8 } 9 Log.e(ScopeTAG, "testAlso sub = $sub") 10 11 //2,静态类、伴生对象 12 val a1 = Int.also { 23 } 13 Log.e(ScopeTAG, "testAlso a1 = $a1") 14 val a2 = Also.apply { value = 39 } 15 Log.e(ScopeTAG, "testAlso a2 = $a2") 16 17 //3,空对象 18 val nil : Also? = null 19 nil?.also { 20 it.value = 122 21 } 22 Log.e(ScopeTAG, "testAlso nil = $nil") 23 24 }
结果
testAlso sub = hello
testAlso a1 = kotlin.jvm.internal.IntCompanionObject@6a122e8
testAlso a2 = value = 39
testAlso nil = null
9. takeIf、takeUnless
9.1 作用
1 /** 2 * Returns `this` value if it satisfies the given [predicate] or `null`, if it doesn't. 3 */ 4 @kotlin.internal.InlineOnly 5 @SinceKotlin("1.1") 6 public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? { 7 contract { 8 callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) 9 } 10 return if (predicate(this)) this else null 11 } 12 13 /** 14 * Returns `this` value if it _does not_ satisfy the given [predicate] or `null`, if it does. 15 */ 16 @kotlin.internal.InlineOnly 17 @SinceKotlin("1.1") 18 public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? { 19 contract { 20 callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) 21 } 22 return if (!predicate(this)) this else null 23 }
takeIf 以对象为参数,判断它是否满足某个条件,满足返回对象本身,否则返回null
takeUnless 与takeIf相反,判断是否不满足
9.2 测试
1 fun testTake(){ 2 3 val now = SystemClock.elapsedRealtime() 4 val tif = now.takeIf { 5 Log.e(ScopeTAG,"testTake takeIf now = $it") 6 it % 2 == 0L 7 } 8 Log.e(ScopeTAG,"testTake tif = $tif") 9 10 val tus = now.takeUnless { 11 Log.e(ScopeTAG,"testTake takeUnless now = $it") 12 it % 2 == 0L 13 } 14 Log.e(ScopeTAG,"testTake tus = $tus") 15 }
结果
testTake takeIf now = 5769124
testTake tif = 5769124
testTake takeUnless now = 5769124
testTake tus = null
对象配置并且计算结果