使用Kotlin泛型读取Android SharedPreferences并转换类型的问题
问题原因分析
- 泛型擦除导致TypeToken失效:普通泛型函数中的
T在运行时会被JVM擦除,无法获取T的具体类型信息,因此object : TypeToken<T>() {}.type无法生成正确的类型标识,抛出"not enough information about type"错误。 value as? T转换无效:同样受泛型擦除影响,运行时无法判断String能否转换为目标T类型,只有当T本身是String时转换才会成功,其他场景都会返回null,这个分支实际作用有限。
解决方案:使用inline reified泛型
通过inline函数结合reified关键字,让编译器在编译阶段保留T的具体类型信息,彻底解决泛型擦除带来的问题。修改后的代码如下:
inline fun <reified T> get(key: String): T? { val value = sharedPreferences.getString(key, "") ?: "" if (value.isEmpty()) return null // 直接处理T为String类型的场景 if (T::class == String::class) { @Suppress("UNCHECKED_CAST") return value as T } return try { Gson().fromJson(value, object : TypeToken<T>() {}.type) } catch (exc: Exception) { null } }
代码说明
inline:将函数代码内联到调用位置,既避免函数调用开销,又为reified泛型提供底层支持。reified:强制编译器保留T的具体类型信息,使得TypeToken<T>和T::class能正确识别目标类型。- 显式判断
String类型:直接处理T为String的场景,替代原来无效的类型转换判断。
调用方式保持不变,无需额外修改:
preferences.get<UserInfoBuilder>(PreferencesCoreKeys.USER_INFO)
备选方案:显式传递Type参数
如果不想使用inline reified,可以在调用时手动传递目标类型的Type对象:
fun <T> get(key: String, type: Type): T? { val value = sharedPreferences.getString(key, "") ?: "" if (value.isEmpty()) return null if (type == String::class.java) { @Suppress("UNCHECKED_CAST") return value as T } return try { Gson().fromJson(value, type) } catch (exc: Exception) { null } }
调用时需要生成对应TypeToken:
preferences.get<UserInfoBuilder>( PreferencesCoreKeys.USER_INFO, object : TypeToken<UserInfoBuilder>() {}.type )
这种方式虽然可行,但调用时需要额外编写TypeToken代码,不如inline reified方案简洁。
内容的提问来源于stack exchange,提问作者Vladimir Fisher
相关产品推荐
相关产品推荐

