You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Kotlin泛型读取Android SharedPreferences并转换类型的问题

解决SharedPreferences泛型转换与Gson TypeToken问题

问题原因分析

  1. 泛型擦除导致TypeToken失效:普通泛型函数中的T在运行时会被JVM擦除,无法获取T的具体类型信息,因此object : TypeToken<T>() {}.type无法生成正确的类型标识,抛出"not enough information about type"错误。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 04:50:28