【问题标题】:How to use functional types or lambdas as type for Kotlin's context receivers?如何使用函数类型或 lambdas 作为 Kotlin 上下文接收器的类型?
【发布时间】:2022-10-13 16:28:13
【问题描述】:
package yamin

typealias Foo = () -> Unit

fun main() {
    bar {
        baz()
    }
}

fun bar(foo: Foo) {
    foo()
}

context(Foo)
fun baz() {
    //
}

我尝试将 lambda 类型用于函数的上下文,这在这一点上似乎很好,但是当我尝试在该上下文中调用该函数时,或者至少我认为我失败的上下文是 Kotlin 的编译器向我显示baz() 的此错误:

No required context receiver found: Cxt { context((yamin.Foo /* = () -> kotlin.Unit */)) public fun baz(): kotlin.Unit defined in yamin in file Main.kt[SimpleFunctionDescriptorImpl@7b5e305f] }

也许我误解了 Kotlin 的上下文接收器,或者我期待它不是为它设计的。我只想创建一个只能在特定上下文中调用的函数,并且在本示例中只能在特定 lambda 中调用。

【问题讨论】:

    标签: kotlin lambda kotlin-context-receivers


    【解决方案1】:

    要制作只能在特定范围内调用的函数,请给函数本身一个接收器。而不是typealias Foo = () -> Unit,您可以使用typealias Foo = MyReceiver.() -> Unit 之类的东西。

    您可以使用常规扩展接收器执行此操作,但也可以使用上下文接收器。

    这是一个适合我的简单版本:

    typealias Foo = BarScope.() -> Unit
    
    fun main() {
        bar {
            baz()
        }
    }
    
    object BarScope
    
    fun bar(foo: Foo) {
        BarScope.foo()
    }
    
    context(BarScope)
    fun baz() {
    
    }
    

    【讨论】:

      【解决方案2】:

      就像现在一样,您的 baz 可以这样调用:

      val someFoo: Foo = {}
      with(someFoo) {
          baz()
      }
      

      通过使用with,我将() -> Unit 的实例带入上下文,因此someFoo 成为上下文接收器,我可以使用它调用baz。在baz 中,我可以使用this@Foo 访问此someFoo。这就是上下文接收器应该如何工作的。

      如果你希望baz只能在bar的lambda中被调用,bar的lambda需要使用receiver参数提供上下文接收器,类似于with的lambda声明@ 987654334@。

      object Foo
      
      fun main() {
          bar {
              baz()
          }
      }
      
      fun bar(foo: Foo.() -> Unit) {
          Foo.foo()
      }
      
      context(Foo)
      fun baz() {
          // ...
      }
      

      我将此处的实际上下文更改为 object,因为据我所知,bar 的 lambda 没有什么特别之处。当然,如果您愿意,可以通过将Foo 更改为包含数据的类,使其向baz 提供额外信息。

      请注意,这并不妨碍某人这样做:

      with(Foo) {
          baz()
      }
      

      如果不在bar 之外添加另一层,很难防止这种情况发生,例如:

      // note that bar is no longer a global function
      import bar.Foo.Companion.bar
      
      class Foo private constructor() {
          companion object {
              fun bar(foo: Foo.() -> Unit) {
                  Foo().foo()
              }
          }
      }
      
      context(Foo)
      fun baz() {
          // ...
      }
      

      你也可以做一些简单的事情来防止它从包外发生:

      sealed interface FooInterface
      
      private object Foo: FooInterface
      
      fun bar(foo: FooInterface.() -> Unit) {
          Foo.foo()
      }
      
      context(FooInterface)
      fun baz() {
          // ...
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-11-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-08
        • 2022-11-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多