Home > OS >  Kotlin function call deference between () and {} when lambda as last parameter
Kotlin function call deference between () and {} when lambda as last parameter

Time:11-03

import io.vertx.core.Vertx
import io.vertx.core.http.HttpMethod
import io.vertx.ext.web.Router
import io.vertx.ext.web.handler.CorsHandler

class RestfulServer(
    vertx: Vertx,
    private val ipAddress: String,
    private val port: Int
) {
    private val httpServer = vertx.createHttpServer()
    private val router: Router = Router.router(vertx)

    init {
        corsHandling()
        createRouter()
    }

    private fun corsHandling(): Route =
        router.route().handler {
            CorsHandler
                .create("*")
                .allowedMethods(mutableSetOf(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS))
        }

    private fun createRouter() =
        router.get("/").blockingHandler { ctx ->
            val response = ctx.response()
            response.putHeader("content-type", "application/json")
            response.end("""{}""")
        }

    fun listen() {
        httpServer.requestHandler(router).listen(port, ipAddress)
    }

    fun close() {
        httpServer.close()
    }
}

When I run the above code, the rest API call hangs in the browser, But if I comment out the function corsHandling(), everything works fine.

I found that it's not a problem with CorsHandler but with how I call that function in kotlin.

Working function:

private fun corsHandling(): Route =
        router.route().handler( // here I use ()
            CorsHandler
                .create("*")
                .allowedMethods(mutableSetOf(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS))
        )

This one hangs:

private fun corsHandling(): Route =
        router.route().handler{ // here I use {}
            CorsHandler
                .create("*")
                .allowedMethods(mutableSetOf(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS))
        }

As you can see the only difference is {} instead of () in router.route().handler call. In kotlin, you can omit the function call if the lambda is your last argument.

Might be this question more to Kotlin instead of Vert.x

It's the function definition of handler https://vertx.io/docs/apidocs/io/vertx/ext/web/Route.html#handler-io.vertx.core.Handler-


The actual problem is I'm calling handler function like handler({{lambda}})

@ivo has the answer already but just clarify with a simple example,

fun takesSingleLambda(func: ()-> Unit) {
    func()
}

fun main() {
    println("1")
    takesSingleLambda(
        returnsLambda()
    )
    println("2")
    takesSingleLambda{
        returnsLambda()
    }
    println("3, which does exactly the same as 2")
    takesSingleLambda({
        returnsLambda()
    })
}

fun returnsLambda() = { //this is similar to CorsHandler.create("*").allowedMethods(mutableSetOf(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS))
    println("executing")
}

CodePudding user response:

To say it simple:

functionName{

}

is identical to

functionName({

})

, not to

functionName(

)

So when you write

    router.route().handler{
        CorsHandler
            .create("*")
            .allowedMethods(mutableSetOf(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS))
    }

you are actually writing

    router.route().handler({
        CorsHandler
            .create("*")
            .allowedMethods(mutableSetOf(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS))
    })

Which is, you are wrapping a lambda in another lambda. When it then tries to execute the handler it simply is creating the handler that you wanted it to handle, instead of executing it. I hope that makes sense.

CodePudding user response:

I will use an example from Android and on click listeners on Views.

Java code for onClick listener

interface OnClickListener {
    void onClick(View view)
}

OnClickListener listener = new OnClickListener() {
    override void onClick(View view) {
        //on click stuff
    }
}

Inside OnClick method you have a reference to the View from which the event originated. The same thing can be created in Kotlin with {}.

val listener: OnClickListener = {
    // Where is the view reference?
}

It is implicit argument of the lambda function

val listenerExplicit: OnClickListener = { view -> 
    // there it is
}

So if you would want to apply it to a Button

button.setOnClickListener {
    // This is already the inside on the onClick method
}

So when you have this:

private fun corsHandling(): Route =
    router.route().handler {
        CorsHandler
            .create("*")
            .allowedMethods(mutableSetOf(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS))
    }

You create a handler {} and all that handler does is create CorsHandler and just throws it away.

This should work as well but just shows what is NOT happening and what you want to happen.

private fun corsHandling(): Route =
    router.route().handler { event ->
        val fooHandler = CorsHandler
            .create("*")
            .allowedMethods(mutableSetOf(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS))
        fooHandler.handle(event)
    }
  • Related