Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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)  } 
like image 463
Jire Avatar asked Nov 08 '15 04:11

Jire


People also ask

Can we use lambda without functional interface?

If you could assign a lambda expression to an interface having more than one abstract method (i.e. a non functional interface), the lambda expression could only implement one of the methods, leaving the other methods unimplemented.

Is lambda only for functional interfaces?

No, all the lambda expressions in this code implement the BiFunction<Integer, Integer, Integer> function interface. The body of the lambda expressions is allowed to call methods of the MathOperation class. It doesn't have to refer only to methods of a functional interface.

Is lambda a function or interface?

Functions The most simple and general case of a lambda is a functional interface with a method that receives one value and returns another.


Video Answer


2 Answers

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) 
like image 101
Andrey Breslav Avatar answered Sep 17 '22 18:09

Andrey Breslav


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).

like image 34
Nikolay Kulachenko Avatar answered Sep 19 '22 18:09

Nikolay Kulachenko