Confusion with generic. How to compile?


#1

 

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


#2

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.


#3

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?


#4

  

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
}


#5

 

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?


#6

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. */