Confusion with generic. How to compile?

 

package unitcraftcard.test

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

abstract class Msg
class PrintMsg(val s:String) : Msg()
class MsgRule<in T:Msg>(val prior:Int,val apply: (T) -> Unit)

val rulesInfo = HashMap<KClass<out Msg>, MutableList<MsgRule<Msg>>>()

inline fun <reified T:Msg> on(prior: Int, @noinline apply: T.() -> Unit) {
  rulesInfo.getOrPut(javaClass<T>().kotlin){ ArrayList<MsgRule<Msg>>() }.add(MsgRule(prior,apply))
}

fun main(args: Array<String>) {
  on<PrintMsg>(1){
  println(s+" 1")
  }
  on<PrintMsg>(0){
  println(s+" 0")
  }

  val msg = PrintMsg(“hello”)

  rulesInfo.values().forEach { it.sortBy{ it.prior } }

  rulesInfo[msg.javaClass.kotlin].forEach { it.apply(msg)  }
}



How to compile this? I stuck with:

Error:(14, 76) Kotlin: None of the following functions can be called with the arguments supplied: public abstract fun add(index: kotlin.Int, element: unitcraftcard.test.MsgRule<unitcraftcard.test.Msg>): kotlin.Unit defined in kotlin.MutableList public abstract fun add(e: unitcraftcard.test.MsgRule<unitcraftcard.test.Msg>): kotlin.Boolean defined in kotlin.MutableList

Try to specify some type parameters like this:

 
rulesInfo.getOrPut(javaClass<T>().kotlin){ ArrayList<MsgRule<Msg>>() }.add(MsgRule<Msg>(prior, apply))

You'll get an error on apply meaning you can't use it where (Msg) -> Unit is expected.

The error reporting should be better in this case.

No. This is not compiles too.

I want to write DSL for subscribers. But I cant compile. I simplify code by max:

package rule

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

abstract class Msg

val rules = HashMap<KClass<out Msg>, (Msg) -> Unit>()

inline fun <reified T:Msg> on(@noinline apply: T.() -> Unit) {
  rules[javaClass<T>().kotlin] = apply
}

fun main(args: Array<String>) {
  class PrintMsg(val str:String) : Msg()

  on<PrintMsg>{
  println(str)
  }

  rules[PrintMsg::class](PrintMsg(“hello”))
}



Compiler:

Warning:(11, 41) Kotlin: Parameter ‘apply’ is never used
Error:(12, 5) Kotlin: Type inference failed: Cannot infer type parameter V in fun <K, V> kotlin.MutableMap<K, V>.set(key: K, value: V): V?
None of the following substitutions
receiver: kotlin.MutableMap<kotlin.reflect.KClass<out rule.Msg>!, ((rule.Msg) -> kotlin.Unit)!>  arguments: (kotlin.reflect.KClass<out rule.Msg>!,((rule.Msg) -> kotlin.Unit)!)
receiver: kotlin.MutableMap<kotlin.reflect.KClass<out rule.Msg>!, T.() -> kotlin.Unit>  arguments: (kotlin.reflect.KClass<out rule.Msg>!,T.() -> kotlin.Unit)
can be applied to
receiver: java.util.HashMap<kotlin.reflect.KClass<out rule.Msg>, (rule.Msg) -> kotlin.Unit>  arguments: (kotlin.reflect.KClass<T>,T.() -> kotlin.Unit)


I try some variants but I end up with:

package rule2

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

abstract class Msg

val rules = HashMap<KClass<out Msg>, Any>() // How properly declare this?

inline fun <reified T:Msg> on(@noinline apply: T.() -> Unit) {
  rules[javaClass<T>().kotlin] = apply
}

fun main(args: Array<String>) {
  class PrintMsg(val str:String) : Msg()

  on<PrintMsg>{
  println(str)
  }

  (rules[PrintMsg::class] as ((Msg) -> Unit))(PrintMsg(“hello”)) // How to avoid unchecked cast?
}



How to compile this code without unchecked cast?

  

class Rules {
  val rules = HashMap<KClass<out Msg>, Any>()

  fun set<T: Msg>(key: KClass<out T>, rule: (T) -> Unit) {
  rules[key] = rule
  }

  fun get<T: Msg>(key: KClass<out T>): (T) -> Unit {
  @suppress(“UNCHECKED_CAST”)
  return rules[key] as (T) -> Unit
  }
}

val rules = Rules()

inline fun <reified T:Msg> on(@noinline apply: T.() -> Unit) {
  rules[javaClass<T>().kotlin] = apply
}

fun main(args: Array<String>) {
  class PrintMsg(val str:String) : Msg()

  on<PrintMsg>{
  println(str)
  }

  rulesPrintMsg::class
}


 

abstract class Msg

val msgs = HashMap<KClass<out Msg>, Msg>()

inline fun <reified T:Msg> addMsg(@noinline msg: T) {
  msgs[javaClass<T>().kotlin] = msg
}



Why Kotlin dont compiles this?

abstract class Msg

val fns = HashMap<KClass<out Msg>, (Msg)->Unit>()

inline fun <reified T:Msg> addFn(@noinline fn: (T)->Unit) {
  fns[javaClass<T>().kotlin] = fn
}



What is difference between T and (T)->Unit for reified T?

The errors when type inference failed are not easy to understand sometimes. So when type inference failed it's helpful to specify type arguments to see what's wrong.

 
inline fun <reified T:Msg> addFn(@noinline fn: (T)->Unit) {
    fns.set<KClass<out Msg>, (Msg)->Unit>(javaClass<T>().kotlin, fn)
}

For your example there is an error on fn: Type mismatch: inferred type is (T) -> Unit but (Msg) -> Unit was expected.

And, indeed, (T) -> Unit is not a subtype of (Msg) -> Unit. You can’t pass (T) -> Unit where (Msg) -> Unit is expected.

As for your first example, T is a subtype of Msg, so everything compiles.

/* See Generics, declaration-site variance for more information: http://kotlinlang.org/docs/reference/generics.html.
Function interface is declared with ‘in’ argument: Function1<in P1, out R>.
Let’s ConcreteMsg inherits Msg. Then (Msg) -> Unit is a subtype of (ConcreteMsg) -> Unit but not vice versa.
You can’t pass (ConcreteMsg) -> Unit where (Msg) -> Unit is expected. But imagine you could. Then you’d invoke your function wanting only ConcreteMsg as argument on any Msg. No good. */