Reified generics on class level

Hello,

As far as I could find, this question has not been posted before. If it has, I would be happy to view that forum post.

Edit: I just found Can generic parameters be reified at class level?, no idea why it did not pop up in the first place…

I have a use case where there is a collection of items, with some generic type. I need to filter the items in the collection based on their generic type, and then invoke some specific function on the items using data that only compiles if the types I just checked (filtered on) are correct.

In code:

class Item<T> { 
  val clazz : Class<T>
  // other properties
  fun operation (t: T) { /* ... */ }
}

val value: Long = ...
val specificClass: Class<Long> = ...
val items : List<Item<*>> = ....
items.filter { it.clazz == specificClass }
  .forEach { it.operation(value) } 
          // ^^^^^^^^^^^^ Here I would need to cast each item 
          // to Item<Long>, even though I just checked that 
          // it.clazz == Long::class

I would like to have that code like:

val items : List<Item<*>> = ....
items.filterInstanceOf<Item<Long>>()
  .forEach { it.operation(value) } // <-- Smart cast to Item<Long>

At the moment this is not possible, because classes cannot have reified generic parameters (only inline functions can have them). Would a language extension be possible where classes with some of their generic parameters annotated as reifed be available at runtime?

A small example of the code it could generate automatically:

class Item<reified T>

// ... would be equivalent to ... 

class Item<T> {
  val genericClassArgument0: Class<T>
}

(just like componentN() for data classes).

Then, we would need to fill the parameter for every constructor call. That could work in the following way (just like an inline function):

class Item<reified T>(val value: Long)
val item = Item<UUID>(Random.nextLong())
// ... this would actually call ...
//   Item(UUID::class, Random.nextLong())
// and bytecode would just contain the constructor with the Class<*>, Long arguments

I hope this makes the idea clear. Thanks in advance for any reactions.

Something like that was discussed multiple times and it makes sense in the long run. Still, I do not think that it is really needed right now, since it is rarely used and could be replaced by two lines of code:

class Item<T: Any> private constructor(val type : KClass<out T>) {
  companion object{
    inline operator fun <reified T: Any> invoke() = Item(T::class)
  } 
} 
3 Likes

Thank you for replying. I understand that this would work.

The code works, but implementing a function like

fun <T:Any> Collection<Item<*>>.filterGenericInstance<T>() : List<Item<T>> {
  // ...
}

would still require (unchecked) casting from Item<*> to Item<T>, because during construction you still lose the information that the type T of Item<T> is actually stored in runtime.

My point is that by building such functionality into the language, we would have both the construction and the type-safe filtering of such generic types out of the box.