You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 03:17:41