如何在Dart中通过父类泛型函数实例化子类?
Dart模拟枚举特性:带继承的const类实例化方案
需要用一组类模拟枚举特性,但Dart不允许继承enum,因此采用含final int字段的const小型类实现。核心需求:
- 父类定义默认行为,子类可按需重写
- 实现基于父类泛型的函数,能动态创建对应子类的实例
之前尝试的失败方案
1. 直接通过泛型类型调用构造函数
Dart不允许直接通过泛型类型T调用构造方法:
class A { final int x; A(this.x); } class B extends A { B(this.x); } T newOne<T extends A>(int z) { return T(z); // 错误:无法直接调用泛型类型的构造函数 }
2. 父类与子类定义工厂方法
尝试通过泛型调用类的工厂方法,同样无法通过编译:
class A { final int x; A(this.x); factory A.newOne(int z) { return A(z); } } class B extends A { B(this.x); factory B.newOne(int z) { return B(z); } } T newOne<T extends A>(int z) { return T.newOne(z); // 错误:无法解析泛型类型的工厂方法 }
3. 工厂方法类型别名
尝试用类型别名存储工厂方法,但语法错误:
typedef AFactory<T extends A> = T Function(int x); class A { final int x; AFactory factory; A(this.x): factory = A.newOne; factory newOne(int z) { return A(z); } // 错误:factory不是类成员类型 } class B extends A { B(this.x): factory = B.newOne; // 错误 factory newOne(int z) { return B(z); } }
可行实现方案
方案1:静态映射表关联泛型与构造函数
维护一个泛型类型到构造函数的映射表,泛型函数通过映射获取对应构造方法创建实例:
abstract class A { final int x; const A(this.x); } class B extends A { const B(super.x); } class C extends A { const C(super.x); } // 手动维护类型到构造函数的映射 final Map<Type, A Function(int)> _typeToFactory = { B: (x) => B(x), C: (x) => C(x), }; T createInstance<T extends A>(int x) { final factory = _typeToFactory[T]; if (factory == null) { throw ArgumentError('不支持的类型:$T'); } return factory(x) as T; } // 使用示例 void main() { final b = createInstance<B>(1); final c = createInstance<C>(2); print(b.x); // 输出1 print(c.x); // 输出2 }
优缺点:实现简单,支持const构造;但新增子类时必须手动更新映射表,扩展性一般。
方案2:子类主动注册构造函数
在父类中定义静态注册方法,子类初始化时注册自身的构造函数,泛型函数通过父类注册表获取构造方法:
abstract class A { final int x; const A(this.x); static final Map<Type, A Function(int)> _registry = {}; // 注册子类构造函数 static void register<T extends A>(A Function(int) factory) { _registry[T] = factory; } // 泛型创建实例方法 static T create<T extends A>(int x) { final factory = _registry[T]; if (factory == null) { throw ArgumentError('类型$T未注册'); } return factory(x) as T; } } class B extends A { const B(super.x); // 子类注册自身构造 static void init() { A.register<B>((x) => B(x)); } } class C extends A { const C(super.x); static void init() { A.register<C>((x) => C(x)); } } // 使用前需初始化子类注册 void main() { B.init(); C.init(); final b = A.create<B>(1); final c = A.create<C>(2); print(b.x); // 输出1 print(c.x); // 输出2 }
优缺点:子类自主管理注册,父类无需修改;但需要在使用前调用子类的init方法,增加了初始化步骤。
方案3:类型判断结合Mixin(支持const)
如果需要严格保持const构造,可以通过Mixin封装创建逻辑,在泛型函数中逐个判断类型:
abstract class A { final int x; const A(this.x); } mixin EnumCreator<T extends A> { static T create<T extends A>(int x) { if (T == B) return const B(x) as T; if (T == C) return const C(x) as T; throw ArgumentError('不支持的类型:$T'); } } class B extends A with EnumCreator<B> { const B(super.x); } class C extends A with EnumCreator<C> { const C(super.x); } // 使用示例 void main() { const b = EnumCreator.create<B>(1); const c = EnumCreator.create<C>(2); print(b.x); // 输出1 print(c.x); // 输出2 }
优缺点:完全支持const构造,实例编译期确定;但子类数量较多时,create函数需要逐个添加类型判断,维护成本较高。
内容的提问来源于stack exchange,提问作者Lee Daniel Crocker
相关产品推荐
相关产品推荐

