How to get class of generic type parameter in Kotlin
Solution 1
You can only get the class on reified variables. The same thing happens in java, but with a slightly different message:
public <T> void x(){
T t = T.class.newInstance();
}
In Java, you'd solve this like:
public <T> void x(Class<T> cls){
T t = cls.newInstance();
}
The same applies to Kotlin, and any calls. You'd need to get a class instance in most cases. However, Kotlin supports reified generics using a keyword, but only on inline generic functions. You could pass a class, but in functions, it's really easy just using the reified keyword.
As in you can't declare a class with reified generics, which means this is invalid:
class SomeClass<reified T>
But it is valid for inline functions, meaning you can do:
inline fun <reified T> someFunction()
So you have two options. But since you extend a listener, the first option of adding the generics to the function isn't an option. You can't override a non-generic method with generics. It won't compile.
Which leaves the second option, which unfortunately is rather hackish; passing the class to the constructor. So it should look like this:
class FirebaseDBRepo<T : Any>(val child: String, private val cls: Class<T>) {
Now, I don't use Firebase, so I have no clue what classes you'd pass, so for this next example, I just use String
.
Kotlin supports some type minimization without going over to raw types. This:
val t = FirebaseDBRepo<String>("", String::class.java)
Could be shortened to this:
val t = FirebaseDBRepo("", String::class.java)
The inferred type in both cases is FirebaseDBRepo<String>
.
Solution 2
Since you are running on the JVM, type erasure is a thing. This means (in simplified terms), that during compilation, the generics are simply ignored. Therefore, you cannot get the class of T, as the JVM doesn't even know what you mean by "T".
Kotlin uses a clever trick to come around this limitation in some cases. When you are using inline functions, the compiler does not call the function you defined, but instead, copies the whole body to the location where you called it. This can only be done for inline functions. Not classes.
There is a workaround tough: Just add private val classT: Class<T>
to the constructor and use the parameter instead!
Comments
-
Ricardo almost 2 years
I would like get the class property from a generic type
T
. I've decided to extend toAny
but I'm getting an error. https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html#extension-propertiesI have the following code:
class FirebaseDBRepo<T : Any>(val child:String) { private var callback: FirebaseDatabaseRepositoryCallback<T>? = null private val ref: DatabaseReference private val listener = object : ValueEventListener { override fun onDataChange(dataSnapshot: DataSnapshot) { //T::class.java is showing the error cannot use t as reified type parameter use class instead val gameDS = dataSnapshot.getValue(T::class.java) callback!!.onSuccess(gameDS!!) } override fun onCancelled(databaseError: DatabaseError) { } } init { ref = FirebaseDatabase.getInstance().reference.child(child) } fun addListener(callback: FirebaseDatabaseRepositoryCallback<T>) { this.callback = callback ref.addValueEventListener(listener) } fun removeListener() { ref.removeEventListener(listener) } }
-
Drew Nutter about 4 yearsI would love it if Kotlin could figure out what to set
cls
to if I usedval t = FirebaseDBRepo<String>("")
. Especially because sometimes I have a class definition that's more likeclass FirebaseDBRepo<T : Any>(val child: T, private val cls: Class<T>)
. Kotlin can figure out T from child. In that situation I want to be able to doFirebaseDBRepo("")
.