将 lambda 作为扩展方法传递

Pass lambda as extension method

我最近在学习kotlin,遇到了下面的场景。在 Ktor 服务器中有一个具有以下签名的方法:

fun Route.webSocket(protocol: String? = null, handler: suspend DefaultWebSocketServerSession.() -> Unit) {
    webSocketRaw(protocol) {
        proceedWebSocket(handler)
    }
}

我应该像这样与它互动的地方:

embeddedServer(Netty, 8080) {
        install(Routing) {
            webSocket("/ws") {
                // Handle websocket connection here
            }
        }
}

意思是 websocket 接受 labda,它是 DefaultWebSocketServerSession 的扩展方法并且有它的上下文。我想将此 lambda 转换为处理程序,以便我可以从其他地方传递它,我想它应该看起来像这样:

embeddedServer(Netty, 8080) {
        install(Routing) {
            webSocket("/ws", myHandler::handle)
        }
}

//...
fun suspend handle(context: DefaultWebSocketServerSession): Unit {
    // Handle websocket connection here
}

所以,我的问题是如何将 suspend DefaultWebSocketServerSession.() -> Unit 转换为 (DefaultWebSocketServerSession) -> Unit,或者如何实现具有 suspend DefaultWebSocketServerSession.() -> Unit 签名的处理程序以便我可以从外部传递它?

PS

我知道我能做到

embeddedServer(Netty, 8080) {
        install(Routing) {
            webSocket("/ws") {
                myHandler.handle(this)
            }
        }
}

但这感觉并不优雅

不需要自己转换任何东西。 Kotlin 使用 Receiver 在方法引用、函数文字和函数文字之间进行转换。看这个例子:

class A

class AHandler {
  fun handle(a: A) {
    println("AHandler $a")
  }
}

fun useLambdaWithReceiver(lambda: A.()->Unit) {
  val a = A()
  lambda(a)
}

fun useNormalLambda(lambda: (A)->Unit) {
  useLambdaWithReceiver(lambda)
}

fun main() {
  val handler = AHandler()
  useLambdaWithReceiver {
    println("useLambdaWithReceiver $this")
  }
  useNormalLambda {
    println("useNormalLambda $it")
  }
  useLambdaWithReceiver(handler::handle)
  useNormalLambda(handler::handle)
}

输出:

useLambdaWithReceiver A@174d20a
useNormalLambda A@3c756e4d
AHandler A@2ef5e5e3
AHandler A@6d00a15d

一切都自动编译和转换。 所以,你可以只传递你的处理程序方法,它应该没问题,除非有一个我不知道的带有 suspend 修饰符的错误。