【问题标题】: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() {
// ...
}