如何实现键值类型一一关联的Dart Map类型标注?
Dart中实现键值类型关联的异质Map类型安全存储
核心问题说明
Dart原生的Map<K, V>是同质集合,所有键必须属于同一类型K,所有值必须属于同一类型V,无法直接实现“每个键值对的键与值类型关联,但不同键值对类型可不同”的类型标注。但针对你封装SharedPreferences的实际场景,可以通过封装类型安全的访问层来实现需求。
具体实现方案
1. 基础类型定义
首先定义带泛型的枚举和值包装类:
// 带泛型的持久化数据键枚举 enum PersistentData<T> { data1<int>(), data2<String>(), } // 关联业务类型T与SharedPreferences原始类型S的值包装类 class PersistentDataValue<T, S> { final S rawValue; // 存储到SharedPreferences的原始值 final T Function(S) fromRaw; // 原始值转业务类型 final S Function(T) toRaw; // 业务类型转原始值 PersistentDataValue({ required this.rawValue, required this.fromRaw, required this.toRaw, }); }
2. 类型安全的存储封装类
通过封装泛型方法,隐藏内部的动态类型存储,对外提供严格的类型检查:
class PreferencesStore { // 内部用动态类型存储,对外层屏蔽 final Map<PersistentData, dynamic> _internalStore = {}; // 写入:约束键的泛型T与值的泛型T一致 void set<T, S>(PersistentData<T> key, PersistentDataValue<T, S> value) { _internalStore[key] = value; } // 读取:根据键的泛型T自动推导值的类型 PersistentDataValue<T, S>? get<T, S>(PersistentData<T> key) { return _internalStore[key] as PersistentDataValue<T, S>?; } }
3. 使用示例
void main() { final store = PreferencesStore(); // 合法操作:键与值的泛型类型匹配 store.set( PersistentData.data1, PersistentDataValue<int, int>( rawValue: 2024, fromRaw: (s) => s, toRaw: (t) => t, ), ); store.set( PersistentData.data2, PersistentDataValue<String, String>( rawValue: "hello", fromRaw: (s) => s, toRaw: (t) => t, ), ); // 非法操作:类型不匹配,编译器直接报错 // store.set( // PersistentData.data1, // PersistentDataValue<String, String>(...), // ); // 读取时自动推导类型 final intValue = store.get(PersistentData.data1); final stringValue = store.get(PersistentData.data2); }
其他可选方案
如果需要更紧密的类型关联,可以给枚举成员绑定对应的原始类型S,通过扩展或关联类实现自动类型推导:
// 给枚举添加扩展,绑定每个成员的原始类型S extension PersistentDataExt<T> on PersistentData<T> { Type get rawType { switch (this) { case PersistentData.data1: return int; case PersistentData.data2: return String; } } PersistentDataValue<T, S> createValue<S>(T value) { switch (this) { case PersistentData.data1: return PersistentDataValue<int, int>( rawValue: value as int, fromRaw: (s) => s, toRaw: (t) => t, ) as PersistentDataValue<T, S>; case PersistentData.data2: return PersistentDataValue<String, String>( rawValue: value as String, fromRaw: (s) => s, toRaw: (t) => t, ) as PersistentDataValue<T, S>; } } }
这种方式可以进一步简化存储类的操作,减少重复代码。
总结
原生Map无法实现异质键值对的类型安全标注,但通过封装泛型方法的存储类,可以在保证对外类型安全的同时,灵活处理不同键值对的类型关联,完全满足你封装SharedPreferences的需求。
内容的提问来源于stack exchange,提问作者panthas
相关产品推荐
相关产品推荐

