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

Kotlin中如何利用枚举集合关联类型实现类型安全的数据查询

解决方案:根据集合自动推导类型,避免泛型误用

问题回顾

当前的getItemFromCollection函数使用泛型<T>,允许调用者错误指定与集合不匹配的类型,导致运行时类型转换异常。我们需要让函数根据传入的Collection枚举自动确定返回类型,同时在编译期约束类型匹配。

方案实现

1. 重构Collection枚举,关联类型信息

定义带泛型的接口,让枚举实现该接口,明确每个集合对应的类型:

interface TypedCollection<T> {
    val path: String
    val type: Class<T>
}

enum class Collection(override val path: String, override val type: Class<*>) : TypedCollection<*> {
    USERS("users", User::class.java),
    ITEMS("items", Item::class.java);

    // 强制类型转换,确保与枚举常量的实际类型匹配
    @Suppress("UNCHECKED_CAST")
    fun <T> asTypedCollection(): TypedCollection<T> {
        return this as TypedCollection<T>
    }
}

2. 修改缓存与查询函数

调整缓存类型,实现类型安全的查询函数,让泛型T与集合类型自动关联:

private val CACHE: HashMap<TypedCollection<*>, HashMap<String, Any>> = hashMapOf()

fun <T> getItemFromCollection(id: String, collection: TypedCollection<T>, onSuccess: (T) -> Unit) {
    // 获取当前集合的缓存,不存在则初始化空Map
    val collectionCache = CACHE.getOrPut(collection) { hashMapOf() }

    // 优先检查缓存
    collectionCache[id]?.let { cachedItem ->
        onSuccess(cachedItem as T)
        return
    }

    // 缓存不存在时从数据库查询
    db.collection(collection.path).document(id).get()
        .addOnSuccessListener { document ->
            // 用集合指定的类型解析数据
            val item = document.toObject(collection.type)
            item?.let {
                // 存入缓存
                collectionCache[id] = it
                // 回调返回结果
                onSuccess(it)
            }
        }
}

3. 调用方式

调用时无需手动指定泛型,编译器会自动推导类型,错误类型会在编译期被拦截:

// 正确调用:自动推导类型为User
getItemFromCollection(user_id, Collection.USERS.asTypedCollection()) { user ->
    // user的类型自动是User
}

// 错误调用:编译期直接报错,因为USERS集合对应User类型而非OtherClass
// getItemFromCollection<OtherClass>(user_id, Collection.USERS.asTypedCollection()) { ... }

替代方案(保留原枚举结构)

如果不想引入接口,也可以通过inline reified函数在编译期检查类型匹配:

enum class Collection(val path: String, val type: Class<*>) {
    USERS("users", User::class.java),
    ITEMS("items", Item::class.java);

    @Suppress("UNCHECKED_CAST")
    fun <T> cast(obj: Any): T {
        return obj as T
    }
}

private val CACHE: HashMap<Collection, HashMap<String, Any>> = hashMapOf()

inline fun <reified T> getItemFromCollection(id: String, collection: Collection, onSuccess: (T) -> Unit) {
    // 编译期检查类型是否匹配,不匹配则抛出异常
    require(collection.type == T::class.java) {
        "集合${collection.name}仅支持类型${collection.type.simpleName},不能使用${T::class.simpleName}"
    }

    val collectionCache = CACHE.getOrPut(collection) { hashMapOf() }
    collectionCache[id]?.let {
        onSuccess(collection.cast(it))
        return
    }

    // 数据库查询逻辑
    db.collection(collection.path).document(id).get()
        .addOnSuccessListener { doc ->
            val item = doc.toObject(collection.type) as T
            item?.let {
                collectionCache[id] = it
                onSuccess(it)
            }
        }
}

调用示例:

getItemFromCollection(user_id, Collection.USERS) { user: User ->
    // 处理User对象
}

内容的提问来源于stack exchange,提问作者itaypoo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 13:21:28