I want to get string from SharedPreferences and then with generics convert it to so some class. I have this code:
fun <T> get(key: String): T? {
val value = sharedPreferences.getString(key, "") ?: ""
(value as? T)?.let { return it }
return if (value.isNotEmpty()) {
try {
Gson().fromJson(value, object : TypeToken<T>() {}.type)
} catch (exc: Exception) {
null
}
} else {
null
}
}
And I call this function like this:
preferences.get<UserInfoBuilder>(PreferencesCoreKeys.USER_INFO)
But inside get
fun I see that (value as? T)
is just a String. And calling object : TypeToken<T>() {}.type
gives me an error "not enough information about type".
How to make it work?
CodePudding user response:
Casting does not convert types to other types. It is only a promise you make to the compiler that the instance you are working with already is that other type.
If you call sharedPreferences.getString
, you will always get a String returned. you can't successfully cast Strings to anything besides the types it already is (Any
, CharSequence
, Comparable<String>
, or the nullable versions of those). So even with reified types, the as? T
cast will always fail unless T is one of those. Without reified types, it will succeed at the call site, and then crash later when you try to use the instance as something other than a String.
And Gson must know the concrete type you are requesting, so you need to use reified generics.
inline fun <reified T> get(key: String): T? {
val value = sharedPreferences.getString(key, "") ?: ""
return if (T::class == String::class) {
value as T
} else {
try { Gson().fromJson(value, T::class.java) }
catch(e: Exception) { null }
}
}