How to get a general class of parameters in Kotlin

Firebase snapshot.getValue() expects a call as follows:

 snapshot?.getValue(Person::class.java) 

However, I would like to replace Person universal parameter that is passed to the class through the class declaration, i.e.

 class DataQuery<T : IModel> 

and use this generic parameter to do something like this:

 snapshot?.getValue(T::class.java) 

but when I try to do this, I get a message that

only classes can be used on the left side of a class literal

Is it possible to provide a class constraint for a universal parameter, like in C #, or is there some other syntax that I can use to get type information for a universal parameter?

+38
source share
4 answers

For a class with a common parameter T, you cannot do this because you do not have type information for T, since the JVM erases the type information. Therefore, such code cannot work:

 class Storage<T: Any> { val snapshot: Snapshot? = ... fun retrieveSomething(): T? { return snapshot?.getValue(T::class.java) // ERROR "only classes can be used..." } } 

But you can do this work if type T is validated and used inside the built-in function:

 class Storage { val snapshot: Snapshot? = ... inline fun <reified T: Any> retrieveSomething(): T? { return snapshot?.getValue(T::class.java) } } 

Note that the built-in function, if public can only access public members of the class. But you can have two options for a function: one that receives a class parameter that is not inline, and accesses private internal elements, as well as another built-in helper function that overrides from the inferred type parameter:

 class Storage { private val snapshot: Snapshot? = ... fun <T: Any> retrieveSomething(ofClass: Class<T>): T? { return snapshot?.getValue(ofClass) } inline fun <reified T: Any> retrieveSomething(): T? { return retrieveSomething(T::class.java) } } 

You can also use KClass instead of Class so that callers who are only Kotlin can simply use MyClass::class instead of MyClass::class.java

If you want the class to interact with the built-in method in generics (this means that the Storage class only stores objects of type T ):

 class Storage <T: Any> { val snapshot: Snapshot? = ... inline fun <reified R: T> retrieveSomething(): R? { return snapshot?.getValue(R::class.java) } } 

Reference to reified types in built-in functions: https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters

+37
source

What you need is a reify modifier for your general parameter, you can read about it here. https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters Therefore, if you do something like this:

 inline fun <reified T : Any>T.logTag() = T::class.java.simpleName 

you will get the name of the actual class of the caller, not the "Object".

+19
source

you can get its class type like this

 snapshot?.getValue((this.javaClass .genericSuperclass as ParameterizedType) .actualTypeArguments[0] as Class<T>) 
+6
source

See https://dev.to/cjbrooks12/kotlin-reified-generics-explained-3mie .

 inline fun <reified T : Number> SharedPreferences.getData(key: String): T? { val cls = T::class.java return if (cls.isAssignableFrom(Integer::class.java)) { getInt(key, 0) as T } else if (cls.isAssignableFrom(Long::class.java)) { getLong(key, 0) as T } else { throw IllegalStateException("Unsupported type") } } 
0
source

All Articles