0

I don't know how to specify a function literal where the input parameter can covary so that the function literal can have assigned to it functions that accept subtypes of the input type.

The simplest example I can come up with is that i want to do something like this:

var f: (Number) -> Unit = { number: Number -> println(number) } f = {int: Int -> println(number) } // <-- this does not compile 

These declarations do not work:

var f: (in Number) -> Unit = {number: Number ->} // doesn't compile var f: (out Number) -> Unit = {number: Number ->} // doesn't compile var f: <N: Number> (N) -> Unit = {number: Number ->} // ridiculous 

Here is the context of what I ACTUALLY want to do. I want to create a simple event handler class.

This is my Kotlin code:

class EventHandler() { private val NO_OP = {event: Event -> } private val handlerMap = mutableMapOf<KClass<out Event>, (Event) -> Unit>() // here is the problem declaration fun <E: Event> registerHandler( eventClass: KClass<out E>, handler: (E) -> Unit) { handlerMap[eventClass] = handler // this doesn't compile } fun handle(event: Event) = getHandler(event).invoke(event) fun getHandler(event: Event): (Event) -> Unit = handlerMap[event::class] ?: NO_OP } 

The handlerMap[eventClass] = handler doesn't compile, because the handlerMap accepts as values (Event) -> Unit, and the type of the handler is (E) -> Unit where E is a type parameter that extends Event (<E: Event>).

The Error message is:

Events.kt:[18,9] Type inference failed: Cannot infer type parameter V in operator inline fun <K, V> MutableMap<K, V>.set(key: K, value: V): Unit None of the following substitutions receiver: MutableMap<KClass<out Event>, (Event) -> Unit> arguments: (KClass<out Event>,(Event) -> Unit) receiver: MutableMap<KClass<out Event>, (E) -> Unit> arguments: (KClass<out Event>,(E) -> Unit) can be applied to receiver: MutableMap<KClass<out Event>, (Event) -> Unit> arguments: (KClass<out E>,(E) -> Unit) 

I am using kotlin-maven-plugin:1.1-M03.

2
  • This should work out-of-the-box, without you declaring in or out , and without casting. Otherwise you are doing something wrong and it can fail at runtime. Commented Dec 30, 2016 at 8:50
  • Possible duplicate of Putting a generic lambda into a map Commented Dec 31, 2016 at 14:10

1 Answer 1

1

Ok, I didn't think of this before, but it looks like you can just cast the Function to the type of the literal.

In other words, you can do this

var f: (Number) -> Unit = { number: Number -> println(number) } f = {int: Int -> println(number) } as (Number) -> Unit 

For my actual usecase, I would make the registerHandler function look like this:

fun <E: Event> registerHandler(eventClass: KClass<out E>, handler: (E) -> Unit) { handlerMap[eventClass] = handler as (Event) -> Unit } 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.