Search code examples
kotlinclosuresdeferred-execution

Why do I have to wrap my Kotlin method references in closures?


In kotlin, I'm trying to create a dispatch table:

class Foo {
    fun handleEvent(bytes:ByteArray) {
        // do something fun with the bytes
    }
}

class Bar {
    fun handleEvent(bytes:ByteArray) {
        // do something fun with the bytes
    }
}

foo = Foo()
bar = Bar()

val eventHandlers:HashMap<RemoteEvent, (bytes:ByteArray)->Unit> = hashMapOf(
    0x01 to foo.handleEvent,
    0x02 to bar.handleEvent)

Kotlin doesn't seem to like this, it complains in multiple ways, but the relevant one seems to be be function invocation expected.

I can fix this by wrapping those in closures:

val eventHandlers:HashMap<RemoteEvent, (bytes:ByteArray)->Unit> = hashMapOf(
    0x01 to { bytes -> foo.handleEvent(bytes) },
    0x02 to { bytes -> bar.handleEvent(bytes) })

Is there no other way? Why do I have to the method signatures, which are correct, in closures which are the same? Are closures and methods not on the same footing in Kotlin?


Solution

  • In Kotlin, function references are created with the :: operator (not .).

    Your intended use case can easily be achieved using this:

    val eventHandlers: HashMap<RemoteEvent, (bytes: ByteArray) -> Unit> = hashMapOf(
            0x01 to foo::handleEvent,
            0x02 to bar::handleEvent)
                     // ^
    

    Function references, Kotlin docs

    foo.handleEvent is interpreted as accessing the property called handleEvent (which doesn't exist.)

    foo::handleEvent is a KFunction instance representing the function called handleEvent, and since this matches the lambda signature (ByteArray) -> Unit it works as expected.

    Notice that this is different from e.g. C# or C++, where due to language restrictions, methods and properties/fields cannot share the same name. In these languages . is fine for method groups/function pointers because it is impossible that foo.bar would be ambiguous.