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.

1 Like

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)
  } 
} 
5 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.

1 Like

This is correct. It only works in the “factory method” in other methods that need to use the “T” you have to do as you suggest… such a pitY @darksnake any updates if this will be considered in the future? reified generics are so cool but using them like this… is an halfaway measure.

Here’s a write-once-forever solution that does the trick for any nesting of types (i.e it checks Item<Item<T>>). Note that a factory function is recommended for nicer typing, but isn’t strictly required.

import kotlin.reflect.*
import kotlin.reflect.full.*
fun main() {
  val value: Long = 42
  val value2: Item<String> = Item()
  val items: List<Item<*>> = listOf(Item<Item<String>>(), Item<Item<Int>>(), Item<Any>())
  items.filterIsInstanceT<Item<Long>>()
    .forEach { it.operation(value) } // prints only once, for Any
  items.filterIsInstanceT<Item<Item<String>>>()
    .forEach { it.operation(value2) } // prints two, one for Item<String> and one for Any
}

// Factory function needed per class just to make construction nicer
inline fun <reified T> Item() = Item<T>(typeOf<Item<T>>())
data class Item<in T>(override val type: KType) : Typed {
  fun operation(t: T) { println("$this printed $t") }
}

// Written once, used forever
interface Typed {
  val type: KType
}

inline fun <reified T: Typed> List<*>.filterIsInstanceT(): List<T> = filter { it.isT<T>() } as List<T>
inline fun <reified T: Typed> Any?.isT() = this is T && type.isSubtypeOf(typeOf<T>())

It should print the following:

Item(type=Item<kotlin.Any>) printed 42
Item(type=Item<Item<kotlin.String>>) printed Item(type=Item<kotlin.String>)
Item(type=Item<kotlin.Any>) printed Item(type=Item<kotlin.String>)

Hi,
all the solutions proposed here using a factory are nice. But they do’nt work if you want to use a the class as base class. For example the following example works:

open class B<E:Enum<E>> constructor(val entries: List<E>) {
  companion object {
    inline operator fun <reified E:Enum<E>> invoke() : B(enumEntries<E>())
  }
}

enum class MyEnum { A,B,C,D }

val bMyEnum=B<MyEnum>()

It is ok, but the invoke function is not a constructor so the following is not OK:

class D : B<MyEnum>()

In this case we don’t even have the problem in the first place, because we have a “factory” which knows the type:

class D : B<MyEnum>(enumEntries<MyEnum>())

Also, if using subtypes, there is yet another way to solve this problem. In this case type parameters are not erased entirely and we can access them in runtime without passing these types explicitly. You can check how utilities like “type token” are implemented, but please be aware this is advanced stuff and if there are other alternatives, they could be used instead.

2 Likes