Dart中Resource泛型类型不兼容问题及实现方案咨询
你的核心问题在于泛型约束的对齐错误:原代码中FutureResourceExt的泛型T extends Resource没有明确绑定Resource的泛型参数,导致错误时返回的Resource<dynamic>无法匹配实际需要的Resource<S>类型,强制转换又会因为泛型参数不兼容触发运行时异常。
修正方案
我们需要将扩展和错误处理逻辑明确绑定到Resource<S>的具体泛型实例上,不需要提前知晓S的具体类型,只需要保证错误实例的泛型参数与成功实例一致即可。
1. 调整扩展与错误处理逻辑
将原来的FutureResourceExt改为针对Future<Resource<S>>的扩展,让错误返回的Resource实例泛型参数与原Future的类型一致:
extension FutureResourceExt<S> on Future<Resource<S>> { Future<Resource<S>> useErrorHandler() { return onError((error, stacktrace) => Resource<S>.error("Error: $error")); } }
如果需要保留单独的handleError函数,也可以修改为带泛型参数的版本:
Resource<S> handleError<S>(dynamic error) { return Resource<S>.error("Error: $error"); } // 扩展中调用 extension FutureResourceExt<S> on Future<Resource<S>> { Future<Resource<S>> useErrorHandler() { return onError((error, stacktrace) => handleError<S>(error)); } }
2. 优化Resource类的空安全性(可选)
原代码中data和error的getter使用!强制解包存在崩溃风险,可以优化为更安全的写法:
class Resource<S> { final S? _data; final String? _message; Resource.success(S data) : _data = data, _message = null; Resource.error(String message) : _data = null, _message = message; bool get isError => _message != null; // 仅当非错误时才返回有效数据,否则抛出异常 S get data { if (isError) throw StateError("Cannot access data in error state"); return _data!; } // 仅当错误时才返回错误信息,否则抛出异常 String get error { if (!isError) throw StateError("Cannot access error in success state"); return _message!; } }
修正后的完整代码
/// Fetches data from TestRepository and shows result main () async { final repository = TestRepository(); final resource = await repository.fetchString(); if (resource.isError) { print("| Error: ${resource.error}"); } else { print("| Success: ${resource.data}"); } } /// A class to get Success or Error responses class Resource<S> { final S? _data; final String? _message; Resource.success(S data) : _data = data, _message = null; Resource.error(String message) : _data = null, _message = message; bool get isError => _message != null; S get data { if (isError) throw StateError("Cannot access data in error state"); return _data!; } String get error { if (!isError) throw StateError("Cannot access error in success state"); return _message!; } } /// A sample repository class to use API and handle errors class TestRepository { Future<Resource<String>> fetchString() { return _dummyApiCall().useErrorHandler(); } /// Dummy API Call mimicking Future<Resource<String>> _dummyApiCall() async { await Future.delayed(const Duration(seconds: 1)); if (true) { // to mimic an exception so that we can catch it throw Exception("Error Occurred"); } return Resource.success("Hello World"); } } /// An extension to wrap Resource Futures with error handler extension FutureResourceExt<S> on Future<Resource<S>> { Future<Resource<S>> useErrorHandler() { return onError((error, stacktrace) => Resource<S>.error("Error: $error")); } }
为什么原方案会失败?
原代码中T extends Resource是未指定泛型参数的原始类型,Dart会将其视为Resource<dynamic>。而你的fetchString返回的是Future<Resource<String>>,此时T实际代表Resource<String>,但handleError返回的是Resource<dynamic>,两者属于不同的泛型实例,无法直接赋值;强制转换as T会因为泛型参数不兼容触发运行时异常——Dart中Resource<dynamic>和Resource<String>是不兼容的类型。
内容的提问来源于stack exchange,提问作者rupinderjeet

