如何在Dart泛型中强制实现接口并调用泛型类型构造函数?
Dart泛型方法实现:约束类型并调用构造函数
问题描述
想要创建一个泛型方法,要求泛型类型实现指定接口,同时能调用该类型的构造函数进行实例化。尝试的代码如下:
void main() { var aToto = Toto(); aToto.FooBarUser<Foo>("testing Foo"); aToto.FooBarUser<Bar>("testing Bar"); } class Foo { late String _myString; Foo.fromString(this._myString); String toTheWorld() { return _myString; } } class Bar implements Foo { late String _myString; Bar.fromString(String aString) { _myString = "Bar: $aString"; } @override String toTheWorld() { return _myString; } } class Toto { T FooBarUser<T extends Foo> (String aString) { var myInstance = T.fromString(aString); print(myInstance.toTheWorld()); return myInstance; } }
运行时报错:
The method 'fromString' isn't defined for the type 'Type'.
补充说明:Dart中泛型的extends约束已经包含了“实现接口”的场景(因为Dart中所有类都隐式作为接口存在),所以T extends Foo可以约束所有实现Foo接口的类型(比如Bar implements Foo完全符合该约束),不需要单独使用implements约束。
错误原因
泛型类型参数T只是编译时的类型标记,并非具体类的实例,Dart编译器无法在编译阶段确认所有符合T extends Foo的类型都包含fromString命名构造函数,因此不允许直接通过T.fromString调用构造函数。
解决方案
方案一:传递构造函数作为参数
直接让调用者将目标类型的构造函数(或工厂方法)作为参数传入泛型方法,方法内部通过该函数创建实例:
void main() { var aToto = Toto(); // 传入Foo的命名构造函数 aToto.FooBarUser("testing Foo", (str) => Foo.fromString(str)); // 传入Bar的命名构造函数 aToto.FooBarUser("testing Bar", (str) => Bar.fromString(str)); } class Foo { late String _myString; Foo.fromString(this._myString); String toTheWorld() { return _myString; } } class Bar implements Foo { late String _myString; Bar.fromString(String aString) { _myString = "Bar: $aString"; } @override String toTheWorld() { return _myString; } } class Toto { // 添加参数接收创建实例的函数 T FooBarUser<T extends Foo>(String aString, T Function(String) creator) { var myInstance = creator(aString); print(myInstance.toTheWorld()); return myInstance; } }
方案二:定义抽象工厂接口(进阶)
如果希望所有符合约束的类型都强制实现特定的实例化方式,可以将Foo改为抽象类,定义抽象的工厂构造函数,让子类实现该构造函数:
void main() { var aToto = Toto(); aToto.FooBarUser<ConcreteFoo>("testing Foo"); aToto.FooBarUser<Bar>("testing Bar"); } // 定义抽象类作为接口,包含工厂构造函数 abstract class Foo { factory Foo.fromString(String str) => throw UnimplementedError(); String toTheWorld(); } class ConcreteFoo implements Foo { late String _myString; ConcreteFoo.fromString(this._myString); @override String toTheWorld() => _myString; // 实现抽象工厂构造函数 @override factory Foo.fromString(String str) => ConcreteFoo.fromString(str); } class Bar implements Foo { late String _myString; Bar.fromString(String aString) { _myString = "Bar: $aString"; } @override String toTheWorld() => _myString; // 实现抽象工厂构造函数 @override factory Foo.fromString(String str) => Bar.fromString(str); } class Toto { T FooBarUser<T extends Foo>(String aString) { // 通过Foo的工厂构造函数创建实例,再强转为T var myInstance = Foo.fromString(aString) as T; print(myInstance.toTheWorld()); return myInstance; } }
注意:这种方式需要确保所有子类的工厂构造函数确实返回对应的T类型,避免类型转换错误。
内容的提问来源于stack exchange,提问作者Rsevero
相关产品推荐
相关产品推荐

