Dart泛型继承场景下,子类型作为函数参数为何类型不兼容?
我在理解带继承关系的泛型类型时遇到了困难,以下是我的Dart代码实现:
class EventHubImpl extends EventHub { final Map<EventId, Signal1<Event>> _signals = <EventId, Signal1<Event>>{}; @override Connection registerEventHandler<T extends Event>( final EventId eventId, final void Function(T) handler ) { if (_signals.containsKey(eventId)) { return _signals[eventId]!.connect(handler); } final Signal1<Event> signal = Signal1<Event>(); _signals[eventId] = signal; return signal.connect(handler); } @override void sendEvent(final Event event) { _signals[event.eventId]?.emit(event); } }
我希望创建一个存储Signal的Map,这些Signal可接收Event的任意子类型。registerEventHandler方法接收的handler参数必须是Event的子类型,但两次调用connect时出现错误:
The argument type 'void Function(T)' can't be assigned to the parameter type 'dynamic Function(Event)'.
明明已声明<T extends Event>,为何此处T无法被当作Event处理?
问题原因
Dart的函数类型遵循逆变规则:如果T extends Event,那么void Function(Event)可以安全赋值给void Function(T)(能处理所有Event的函数,自然能处理它的子类T),但反过来void Function(T)不能赋值给void Function(Event)——你的Signal1<Event>.connect需要的是能接收任意Event的函数,但传入的handler只能处理T类型(Event的子类),当sendEvent发送其他Event子类型时,这个handler会无法处理,所以Dart编译器直接报错阻止这种不安全的转换。
解决方案
根据你的设计目标,有三种可行的修正方式:
方式1:让Signal支持逆变类型参数
如果Signal1类的设计允许,将其类型参数声明为逆变(使用in关键字):class Signal1<in T> { Connection connect(void Function(T) handler) { // 原有实现逻辑 } void emit(T event) { // 原有实现逻辑 } }逆变声明后,
Signal1<Event>就能接受void Function(T)类型的handler,同时保证emit时传入的Event能被handler安全处理(需要确保同一个EventId对应的事件都是T类型)。方式2:使用类型断言(需自行保证类型安全)
如果你能确保同一个EventId对应的所有事件都是T类型,可以通过类型断言强制转换handler的类型:return _signals[eventId]!.connect(handler as void Function(Event));注意:这种方式会绕过编译时类型检查,必须自己维护类型一致性,否则运行时可能抛出类型错误。
方式3:调整Map的泛型类型
将Map的类型改为Map<EventId, Signal1<dynamic>>,这样connect可以接受任意类型的handler:final Map<EventId, Signal1<dynamic>> _signals = <EventId, Signal1<dynamic>>{};这种方式实现最简单,但会丢失部分编译时类型检查,需要在
sendEvent时确保事件类型与对应handler匹配。
内容的提问来源于stack exchange,提问作者av4625

