Kotlin: Use a lambda in place of a functional interface?

JavaIntellij IdeaLambdaKotlin

Java Problem Overview


In Java we can do this Events.handler(Handshake.class, hs -> out.println(hs));

In Kotlin however I am trying to replicate the behavior to replace this:

Events.handler(Handshake::class, object : EventHandler<Handshake> {
	override fun handle(event: Handshake) {
		println(event.sent)
	}
})

With a more convenient:

Events.handler(Handshake::class, EventHandler<Handshake> { println(it.sent) })

For some reason in reference to EventHandler:

enter image description here

More preferably however I'd like to use something even shorter like this: Events.handler(Handshake::class, { println(it.sent) })

Or use the advertised feature to use the method like this: Events.handler(Handshake::class) { println(it.sent) }

This is my Events object:

import java.util.*
import kotlin.reflect.KClass

object Events {

	private val map = HashMap<Class<*>, Set<EventHandler<*>>>()

	fun <T : Any> handler(eventType: KClass<T>, handler: EventHandler<T>) {
		handler(eventType.java, handler)
	}

	fun <T> handler(eventType: Class<T>, handler: EventHandler<T>) = handlers(eventType).add(handler)

	fun post(event: Any) = handlers(event.javaClass).forEach { it.handle(event) }

	operator fun plus(event: Any) = post(event)

	private fun <T> handlers(eventType: Class<T>): HashSet<EventHandler<T>> {
		var set = map[eventType]
		if (set == null) {
			set = HashSet<EventHandler<*>>()
			map.put(eventType, set)
		}
		return set as HashSet<EventHandler<T>>
	}

}

And my EventHandler interface:

@FunctionalInterface
interface EventHandler<T> {

	fun handle(event: T)

}

Java Solutions


Solution 1 - Java

Assuming below that you really need EventHandler as a separate interface (e.g. for Java interop). If you don't, you can simply use a type alias (since Kotlin 1.1):

typealias EventHandler<T> = (T) -> Unit

In this case a simple lambda will work right away.

But if you don't want to use a type alias, the issue still stands. It is that Kotlin only does SAM-conversion for functions defined in Java. Since Events.handler is defined in Kotlin, SAM-conversions do not apply to it.

To support this syntax:

Events.handler(Handshake::class, EventHandler<Handshake> { println(it.sent) })

You can define a function named EventHandler:

fun <T> EventHandler(handler: (T) -> Unit): EventHandler<T> 
    = object : EventHandler<T> { 
        override fun handle(event: T) = handler(event) 
    }

To support this syntax:

Events.handler(Handshake::class, { println(it.sent) })

or this:

Events.handler(Handshake::class) { println(it.sent) }

You need to overload the handler function to take a function instead of EventHandler:

fun <T> Events.handler(eventType: Class<T>, handler: (T) -> Unit) = EventHandler(handler)

Solution 2 - Java

A lot of nice things has happened to Kotlin since @andrey-breslav posted the answer. He mentions:

> It is that Kotlin only does SAM-conversion for functions defined in > Java. Since Events.handler is defined in Kotlin, SAM-conversions do > not apply to it.

Well, that's no longer the case for Kotlin 1.4+. It can use SAM-conversion for Kotlin functions if you mark an interface as a "functional" interface:

// notice the "fun" keyword
fun interface EventHandler<T> {
    fun handle(event: T)
}

You can read the YouTrack ticket here: https://youtrack.jetbrains.com/issue/KT-7770. There's also an explanation why Kotlin needs a marker for such interfaces unlike Java (@FunctionalInterface is only informational and has no effect on the compiler).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJireView Question on Stackoverflow
Solution 1 - JavaAndrey BreslavView Answer on Stackoverflow
Solution 2 - JavaNikolay KulachenkoView Answer on Stackoverflow