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
相关产品推荐
相关产品推荐

