Dart重写父类字段用更具体类型时提示非有效重写如何解决
问题原因
Dart的类属性setter方法的参数类型属于逆变位置,子类重写属性时不能直接收窄参数类型,否则违反里氏替换原则:如果用父类Ainterface类型指向ObjA实例,按照父类定义可以给list赋值任意实现Binterface的类的实例,但ObjA的list只能接收ObjB类型,会出现类型不匹配,因此编译器直接禁止这种重写。
解决方案
方案1:给父类增加泛型约束(最安全,推荐)
将父类Ainterface定义为泛型类,限定泛型参数的上限为Binterface,子类可以直接指定具体的实现类型,全程符合类型安全要求。
// 父类增加泛型,T的上限为Binterface abstract class Ainterface<T extends Binterface> { String? name; List<T?>? list; } abstract class Binterface { String? age; int? len; } // 子类指定泛型为ObjB class ObjA extends Ainterface<ObjB> { String? name; List<ObjB?>? list; ObjA({this.name, this.list}); } class ObjB extends Binterface { String? age; int? len; ObjB({this.age, this.len}); }
方案2:使用covariant关键字声明协变
如果你能确保不会通过父类引用给list赋值非ObjB类型的元素,可以用covariant关键字告诉编译器允许本次类型收窄,自行承担类型安全风险。
abstract class Ainterface { String? name; // 用covariant修饰list,允许子类重写时收窄泛型类型 covariant List<Binterface?>? list; } abstract class Binterface { String? age; int? len; } class ObjA extends Ainterface { String? name; List<ObjB?>? list; ObjA({this.name, this.list}); } class ObjB extends Binterface { String? age; int? len; ObjB({this.age, this.len}); }
注意:该方案存在运行时风险,如果通过父类
Ainterface的引用给list赋值了其他Binterface实现类的实例,会触发运行时类型错误。
方案3:不重写父类属性,新增子类专属类型转换getter
如果不想修改父类定义,可以保留父类的list属性,子类单独新增一个getter做类型转换,避免重写冲突:
abstract class Ainterface { String? name; List<Binterface?>? list; } abstract class Binterface { String? age; int? len; } class ObjA extends Ainterface { String? name; // 不重写父类的list,新增专属getter转成ObjB类型列表 List<ObjB?>? get objBList => list?.cast<ObjB?>(); ObjA({this.name, List<ObjB?>? list}) : super.list = list; } class ObjB extends Binterface { String? age; int? len; ObjB({this.age, this.len}); }
内容的提问来源于stack exchange,提问作者Rolster KT
相关产品推荐
相关产品推荐

