Hi all. I have the following two methods inside my...
# getting-started
d
Hi all. I have the following two methods inside my class Foo: fun cancel(userId: String?, event: Cancel, options: EventOptions? = null, extra: MiddlewareExtra? = null) fun ship(userId: String?, event: Ship, options: EventOptions? = null, extra: MiddlewareExtra? = null) With Cancel and Ship defined as: class Cancel : Event<Cancel> {} class Ship : Event<Ship> {} I'd like to create a map of events and their corresponding methods. I tried the following val map = mapOf<KClass<out Event<*>>, (Foo, String?, Event<*>, EventOptions?, MiddlewareExtra?) -> Unit>( Cancel::class to Foo::cancel, Ship::class to Foo::ship ) But this gives me the following error. Required: (Foo, String?, Event<*>, EventOptions?, MiddlewareExtra?) → Unit Found: KFunction5<Foo, String?, Cancel, EventOptions?, MiddlewareExtra?, Unit> Any help is appreciated.
a
can you use three backticks ``` to format your code blocks please? https://slack.com/help/articles/202288908-Format-your-messages
w
First of all, I'm gonna take the assumption that your
Foo
looks like this:
Copy code
class Foo {
  fun cancel(userId: String?, event: Cancel, options: EventOptions? = null, extra: MiddlewareExtra? = null) {}
  fun ship(userId: String?, event: Ship, options: EventOptions? = null, extra: MiddlewareExtra? = null) {}

  val map = mapOf<KClass<out Event<*>>, (Foo, String?, Event<*>, EventOptions?, MiddlewareExtra?) -> Unit>(
    Cancel::class to Foo::cancel,
    Ship::class to Foo::ship
  )
}
You need to understand that function parameters are
in
when used as generics.
(Cancel) -> Unit
is not a subtype of
(Event<*>) -> Unit
. Imagine the following scenario.
Copy code
val ship: Ship = TODO()
val cancelProcessor: (Cancel) -> Unit = TODO()
val someProcessor: (Event<*>) -> Unit = cancelProcessor as (Event<*>) -> Unit
someProcessor(ship) // Will throw runtime exception
Now you can hack around this by just adding casts as follows:
Copy code
val map = mapOf<KClass<out Event<*>>, (String?, Event<*>, EventOptions?, MiddlewareExtra?) -> Unit>(
  Cancel::class to Foo::cancel as (String?, Event<*>, EventOptions?, MiddlewareExtra?) -> Unit,
  Ship::class to Foo::ship as (String?, Event<*>, EventOptions?, MiddlewareExtra?) -> Unit,
)
Which will trick the compiler into allowing this.
d
Thanks for the explanation, not sure if I like the explicit casts though but it will do the trick for now 🙂
w
Yeah, it's verbose. But using such abstractions for these semantics can never be verified to be typesafe by the compiler. At some point you as the developer have to tell the compiler "I know what I'm doing, I'll take responsibility for the potential runtime errors". That's what casts are for 🙂.
I do agree that it's hard to comprehend the `in`/`out` semantics of generics sometimes