zoukankan      html  css  js  c++  java
  • 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

    • 对一个非空(non-null)对象执行代码
    • 将表达式作为变量引入为局部作用域中
    代码块的返回类型  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
     

    对象配置并且计算结果

  • 相关阅读:
    日期时间格式
    input表单加disable的后无法获取其value值
    登录判断,清除中间变量
    弹出层嵌套
    转换qq音乐格式为mp3
    layui双击表格,跳转修改页面
    设置输入电话号码为11位
    layui表格设置,分数合计
    解决数据库传回时间类型为数字
    自定义标签
  • 原文地址:https://www.cnblogs.com/mhbs/p/12113034.html
Copyright © 2011-2022 走看看