Dart使用泛型封装REST API CRUD调用时静态成员访问报错如何解决
报错原因
Dart 静态成员属于类本身,不支持继承,也无法通过泛型约束直接访问。你在泛型方法内使用的Resource本质是Type类型的对象,系统不会将它和你定义的子类静态成员做绑定,因此会提示找不到对应的属性和方法。
可行实现方案
这里提供两种常用方案,你可以根据项目规模选择:
方案1:轻量传参实现(适合中小型项目)
直接将资源名、序列化方法作为参数传入泛型方法,无需额外全局配置。
第一步:调整实体类定义
abstract class RestResource { int id; RestResource({required this.id}); Map<String, dynamic> toJson(); } class ActualResource extends RestResource { final String additional; // 静态资源名、序列化方法保留 static const String name = "resource"; ActualResource({required this.additional, required int id}) : super(id: id); @override Map<String, dynamic> toJson() { return { 'id': id, 'additional': additional }; } static ActualResource fromJson(Map<String, dynamic> json) { return ActualResource( id: json['id'], additional: json['additional'] ); } }
第二步:封装泛型请求方法
Future<Resource> fetch<Resource extends RestResource>( int id, String resourceName, Resource Function(Map<String, dynamic>) fromJson ) async { // 注意原写法Uri.http参数错误,这里改用parse更稳妥 final url = Uri.parse('http://www.someurl.com/api/$resourceName/$id'); final res = await _httpClient.get(url); validateHTTPResponse(res); return fromJson(jsonDecode(res.body)); }
第三步:调用方式
int id = 1234; ActualResource r = await fetch<ActualResource>(id, ActualResource.name, ActualResource.fromJson);
方案2:全局注册实现(适合中大型项目)
提前注册所有实体的元数据,调用时完全不需要传额外参数,和你最初预期的调用方式一致。
第一步:实现资源注册器
// 资源元数据存储类 class _ResourceMeta { final String name; final Function(Map<String, dynamic>) fromJson; _ResourceMeta(this.name, this.fromJson); } // 全局注册器 class RestResourceRegistry { static final Map<Type, _ResourceMeta> _resourceMetaMap = {}; // 注册资源 static void register<Resource extends RestResource>( String name, Resource Function(Map<String, dynamic>) fromJson ) { _resourceMetaMap[Resource] = _ResourceMeta(name, fromJson); } // 获取资源元数据 static _ResourceMeta get<Resource extends RestResource>() { final meta = _resourceMetaMap[Resource]; if (meta == null) throw Exception('未注册的资源类型:$Resource'); return meta; } }
第二步:项目初始化时注册所有实体
void initApp() { RestResourceRegistry.register<ActualResource>(ActualResource.name, ActualResource.fromJson); // 剩下4个实体在这里一次性注册完成即可 }
第三步:封装无额外参数的泛型请求方法
Future<Resource> fetch<Resource extends RestResource>(int id) async { final meta = RestResourceRegistry.get<Resource>(); final url = Uri.parse('http://www.someurl.com/api/${meta.name}/$id'); final res = await _httpClient.get(url); validateHTTPResponse(res); return meta.fromJson(jsonDecode(res.body)) as Resource; }
第四步:调用方式
和你最初想要的写法完全一致:
int id = 1234; ActualResource r = await fetch<ActualResource>(id);
拓展说明
按照上述逻辑,你可以一次性封装完POST/PUT/DELETE四类请求的泛型方法,5个实体只需要完成注册即可,不需要单独写20个方法,代码冗余度会大幅降低。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

