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

Kotlin Android泛型处理SharedPreferences时Long与Int类型异常问题

问题分析与解决思路

你的问题核心在于泛型类型推断错误,导致preferenceValueFlow函数走到了Int类型的分支,而非预期的Long分支,最终引发NumberFormatException。以下是具体原因和解决步骤:

根本原因

当你调用getPreferenceAsFlow("key", null)并指定变量类型为Flow<Long?>时,由于默认值null没有明确的类型信息,加上T::class无法区分可空类型(Long?的KClass与Long的KClass是同一个实例),运行时泛型擦除可能导致类型判断逻辑匹配错误,最终执行了getInteger方法。如果存储的是超出Int范围的Long值,就会触发数字格式异常。


解决步骤

1. 显式指定泛型参数

调用时显式传递泛型类型,避免编译器模糊推断:

val lastAppDataUpdateDate: Flow<Long?> = securePrefs.getPreferenceAsFlow<Long?>("key", null)

2. 改进类型判断逻辑(推荐方案)

使用reified泛型结合typeOf(需依赖kotlin-reflect),可以准确识别可空与非空类型,避免匹配错误:

首先添加kotlin-reflect依赖(Gradle):

implementation "org.jetbrains.kotlin:kotlin-reflect:1.9.20"

然后修改preferenceValueFlow函数:

@Suppress("UNCHECKED_CAST")
private inline fun <reified T> preferenceValueFlow(keyValue: String, defValue: T?): T? {
    return when (typeOf<T>()) {
        typeOf<String>(), typeOf<String?>() -> getString(key = keyValue, defValue as String?) as T?
        typeOf<Boolean>(), typeOf<Boolean?>() -> getBoolean(key = keyValue, defValue ?: false) as T?
        typeOf<Int>(), typeOf<Int?>() -> getInteger(key = keyValue, defValue as Int?) as T?
        typeOf<Long>(), typeOf<Long?>() -> getLong(key = keyValue, defValue as Long?) as T?
        else -> throw IllegalArgumentException("Preference type not supported: ${typeOf<T>().typeName}")
    }
}

3. 无反射依赖的替代方案

如果不想引入kotlin-reflect,可以通过默认值的类型辅助判断,同时保留泛型显式指定:

@Suppress("UNCHECKED_CAST")
private fun <T> preferenceValueFlow(keyValue: String, defValue: T?): T? {
    // 优先用默认值的类型,默认值为null时依赖泛型推断的类型
    val targetClass = defValue?.let { it::class } ?: T::class
    return when (targetClass) {
        String::class -> getString(key = keyValue, defValue as String?) as T?
        Boolean::class -> getBoolean(key = keyValue, defValue?.let { it as Boolean } ?: false) as T?
        Int::class -> getInteger(key = keyValue, defValue as Int?) as T?
        Long::class -> getLong(key = keyValue, defValue as Long?) as T?
        else -> throw IllegalArgumentException("Preference type not supported: ${targetClass.java.name}")
    }
}

4. 验证存储类型一致性

确认存储该key时使用的是putLong(或对应的加密存储方法),而非putInt。如果存储的是Int类型值,即使读取为Long,超出Int范围时仍会引发异常。


内容的提问来源于stack exchange,提问作者Agustin Sivoplás

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 12:23:23