I have a simple helper function to get value from SharedPreferences like this :
operator inline fun <reified T : Any> SharedPreferences.get(key: String, defaultValue: T? = null): T? {
return when (T::class) {
String::class -> getString(key, defaultValue as? String) as T?
Int::class -> getInt(key, defaultValue as? Int ?: -1) as T?
Boolean::class -> getBoolean(key, defaultValue as? Boolean ?: false) as T?
Float::class -> getFloat(key, defaultValue as? Float ?: -1f) as T?
Long::class -> getLong(key, defaultValue as? Long ?: -1) as T?
else -> throw UnsupportedOperationException("Not yet implemented")
}
}
I have used reified type parameter to have switching over class type and since it is an operator function, I should be able to call with the square brackets syntax like below :
val name: String? = prefs[Constants.PREF_NAME]
But, every time I call it, UnsupportedOperationException is thrown indicating function is not able to get class type.
When I attach debugger and evaluate T::class
, It is giving me an error "Cannot use 'T' as reified type parameter. Use a class instead."
What's wrong with my function? I could not catch the mistake. can anyone help?
Edit : The whole class is here and this is where I get the error.
Update: It seems to be Kotlin compiler issue. Track https://youtrack.jetbrains.com/issue/KT-17748 and https://youtrack.jetbrains.com/issue/KT-17748 for updates.
Reified Generics in Kotlin Generics on the JVM are normally implemented through type erasure, meaning generic type arguments of a function is not preserved at runtime. To solve this, Kotlin may use a reified type parameter that can only be used with inline functions.
To access the information about the type of class, we use a keyword called reified in Kotlin. In order to use the reified type, we need to use the inline function. If a function is marked as inline, then wherever the function is called, the compiler will paste the whole body of the function there. Learn more about inline from here.
Therefore this little post is intended to bring some light into the darkness of Kotlin's reified types. In an ordinary generic function like myGenericFun, you can't access the type T because it is, like in Java, erased at runtime and thus only available at compile time.
Using an inline function with reified type parameter T makes it possible to implement our function as follows: There's no need to pass the Class of T additionally, T can be used as if it was an ordinary class. For the client the code looks like this: Inline reified functions are not callable from Java code, whereas normal inline functions are.
The problem is curious, but it seems that Int::class
is not the same as Int?::class
(which is an illegal expression any ways).
When you add the line:
println(T::class)
To the get
method, and call val age: Int? = prefs["AGE", 23]
, you will see that it prints java.lang.Integer
.
It seems like Int?
is translated into java.lang.Integer
.
A possible (but imho kind of hacky) solution is to use the references to Java classes as the cases for when:
operator inline fun <reified T : Any> get(key: String, defaultValue: T? = null): T? {
return when (T::class) {
String::class -> getString(key, defaultValue as? String) as T?
java.lang.Integer::class -> getInt(key, defaultValue as? Int ?: -1) as T?
java.lang.Boolean::class -> getBoolean(key, defaultValue as? Boolean ?: false) as T?
java.lang.Float::class -> getFloat(key, defaultValue as? Float ?: -1f) as T?
java.lang.Long::class -> getLong(key, defaultValue as? Long ?: -1) as T?
else -> throw UnsupportedOperationException("Not yet implemented")
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With