Dart Floor如何正确实现TypeConverter的多层继承层级
我如下定义了ListConverter<T>以搭建类型转换器的继承层级:
abstract class ListConverter<T> extends TypeConverter<List<T>, String> { static const delimiter = "!DELIMITER!"; T fromDB(String databaseValue); String toDB(T value); @override List<T> decode(String databaseValue) { return databaseValue .split(delimiter) .where((element) => element.isNotEmpty) .map((e) => fromDB(e)) .toList(); } @override String encode(List<T> value) { return value.map((e) => toDB(e)).join(delimiter); } }
基于ListConverter<T>,我实现了IntListConverter:
class IntListConverter extends ListConverter<int> { @override int fromDB(String databaseValue) { return int.parse(databaseValue); } @override String toDB(int value) { return value.toString(); } }
原本计划复用ListConverter<T>实现StringListConverter等其他列表转换器,但运行floor_generator时出现如下报错:
[SEVERE] floor_generator:floor_generator on lib/persistence/db/app_database.dart:
Only classes that inherit from TypeConverter can be used as type converters. Make sure use a class that inherits from TypeConverter.
已知直接继承TypeConverter的(B)层级可正常运行,但希望搭建(A)所示的三层继承结构以统一管理各类列表转换器:
- (A)
TypeConverter -> ListConverter -> IntListConverter - (B)
TypeConverter -> IntListConverter
如何在Dart Floor中正确构建TypeConverter的继承层级?
报错核心原因是floor_generator的类型转换器校验逻辑默认仅检查类的直接父类是否为TypeConverter,不会递归遍历多层继承链,因此中间隔了泛型抽象类ListConverter<T>时,生成器无法识别子类的转换器身份。可通过以下两种方案实现三层复用结构:
方案1:给中间基类添加@TypeConverter注解(Floor 1.2.0及以上版本支持)
直接在抽象基类ListConverter<T>上添加@TypeConverter()注解,标记该类为类型转换器基类,生成器就会顺着继承链识别所有子类的转换器身份,原有代码逻辑不需要做其他改动:
@TypeConverter() // 仅需添加这一行注解 abstract class ListConverter<T> extends TypeConverter<List<T>, String> { static const delimiter = "!DELIMITER!"; T fromDB(String databaseValue); String toDB(T value); @override List<T> decode(String databaseValue) { return databaseValue .split(delimiter) .where((element) => element.isNotEmpty) .map((e) => fromDB(e)) .toList(); } @override String encode(List<T> value) { return value.map((e) => toDB(e)).join(delimiter); } }
后续实现StringListConverter等其他列表转换器时,只需要继承ListConverter<T>并实现对应泛型的fromDB、toDB方法即可,不需要额外添加注解。
方案2:使用Mixin抽离通用逻辑(兼容所有Floor版本)
如果使用的Floor版本较低不支持基类注解,可以把列表编解码的通用逻辑抽离为Mixin,让具体转换器类直接继承TypeConverter的同时混入通用逻辑,既满足生成器对直接父类的校验要求,又不需要重复编写列表编解码代码:
// 抽离通用列表转换逻辑为Mixin mixin ListConverterMixin<T> on TypeConverter<List<T>, String> { static const delimiter = "!DELIMITER!"; T fromDB(String databaseValue); String toDB(T value); @override List<T> decode(String databaseValue) { return databaseValue .split(delimiter) .where((element) => element.isNotEmpty) .map((e) => fromDB(e)) .toList(); } @override String encode(List<T> value) { return value.map((e) => toDB(e)).join(delimiter); } } // int列表转换器:直接继承TypeConverter,混入通用逻辑 class IntListConverter extends TypeConverter<List<int>, String> with ListConverterMixin<int> { @override int fromDB(String databaseValue) { return int.parse(databaseValue); } @override String toDB(int value) { return value.toString(); } } // String列表转换器示例 class StringListConverter extends TypeConverter<List<String>, String> with ListConverterMixin<String> { @override String fromDB(String databaseValue) => databaseValue; @override String toDB(String value) => value; }
优先选择方案1,代码结构和初始设计完全一致,改动量最小。
内容的提问来源于stack exchange,提问作者Jaewoo Pyo

