How to get generic parameter class in Kotlin
What you need is reified modifier for your generic param, you can read about it here. https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters So if you do something like that:
inline fun <reified T : Any>T.logTag() = T::class.java.simpleName
you will get name of the actual caller class, not "Object".
For a class with generic parameter T, you cannot do this because you have no type information for T since the JVM erases the type information. Therefore code like this 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 make this work if the type of T is reified and used within an inline function:
class Storage {
val snapshot: Snapshot? = ...
inline fun <reified T: Any> retrieveSomething(): T? {
return snapshot?.getValue(T::class.java)
}
}
Note that the inline function if public can only access public members of the class. But you can have two variants of the function, one that receives a class parameter which is not inline and accesses private internals, and another inline helper function that does the reification 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 that are Kotlin-only can just use MyClass::class
instead of MyClass::class.java
If you want the class to cooperate with the inline method on the generics (meaning that class Storage
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)
}
}
The link to reified types in inline functions: https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters
Using class comparison. See https://stackoverflow.com/a/61756761/2914140.
inline fun <reified T> SharedPreferences.getData(key: String, defValue: Any? = null): T? = when (T::class) { Integer::class -> { val value = getInt(key, (defValue as? Int) ?: 0) as T if (value == 0) defValue as? T else value } Long::class -> { val value = getLong(key, (defValue as? Long) ?: 0L) as T if (value == 0) defValue as? T else value } Boolean::class -> { val value = getBoolean(key, (defValue as? Boolean) ?: false) as T if (value == false) defValue as? T else value } String::class -> getString(key, defValue as? String) as T? else -> throw IllegalStateException("Unsupported type") }
Using
isAssignableFrom
. 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") } }
For Double
in SharedPreferences see https://stackoverflow.com/a/45412036/2914140.
Use:
val s: String? = preferences.getData("key", "")
val i = preferences.getData<Int>("key")
you can get its class type like this
snapshot?.getValue((this.javaClass
.genericSuperclass as ParameterizedType)
.actualTypeArguments[0] as Class<T>)