在HarmonyOS上跑Flutter项目,数据模型越写越多,每个类都得手动写 == 和 hashCode。虽然Dart的Object.hash一行搞定hashCode,但 == 那部分得逐个字段写比较,漏一个就是bug。Equatable这个纯Dart库,完美解决了这个问题。
手工写 == 的痛点
项目里用户模型四个字段,手动写operator==要八行,hashCode一行。但项目不只一个模型:订单模型十几个字段,地址模型七八个,商品模型五六个……每个都得来一套。有一次写订单模型的 ==,漏了shippingAddress字段,导致两个地址不同但其他字段相同的订单被判定相等,查了半天才发现是equals的问题。Dart没有Lombok,急需轻量级方案自动生成这些样板代码。
Equatable的核心思路
Equatable的做法很简单:继承Equatable类,通过props getter告诉它要比较哪些字段,==、hashCode、toString就全自动了。
- class User extends Equatable {
- final String name;
- final int age;
- final String email;
- final String phone;
- const User(this.name, this.age, this.email, this.phone);
- @override
- List<Object?> get props => [name, age, email, phone];
- }
复制代码
省掉十几个样板行,只多了一个props getter。验证测试确认无误后,才放心大面积使用。其原理在基类重写operator==和hashCode,遍历props列表逐个比较,并做了runtimeType类型检查,确保不同子类实例不会相等。
继承场景:别忘了...super.props
项目中AdminUser继承User,多了一个role字段。关键就是用[...super.props, role]展开父类字段。一开始直接写props => [role],结果两个名字不同的AdminUser相等——父类的name、age等字段根本没参与比较。继承Equatable时务必用...super.props展开父类字段。
EquatableMixin:无法继承时使用
当类已经继承了其他类(如Flutter的State),可以用EquatableMixin混入。效果与继承Equatable完全一样,不影响继承链。可以封装一个EquatableState基类,让状态类继承之。
在Set和Map中当key用
值比较让对象可以放心放进Set或当Map的key。以前两个字段值相同的对象因引用不同在Set中占两个位置,用Equatable后按值去重。同样,Map以对象为key时,同值对象也能查到。这在缓存、去重、分组等场景特别实用。
空props的小技巧
props返回空列表时,所有实例都被视为相等。在Bloc模式中,只关心事件发生而不关心具体内容时很有用。
与鸿蒙ArkTS对比
ArkTS(TypeScript超集)中===也是引用比较,但生态没有现成的equatable库。做法有:自己写equals方法,每个类手动写(与Dart手工==一样烦);或用Lodash的isEqual做深比较,性能开销大且包体积大。对比之下,Dart的equatable声明式列出比较字段,精准轻量,无运行时反射,性能开销可忽略。
踩过的坑
mutable props导致脏数据:模型中包含List<String>字段并列入props,修改列表内容后hashCode变了,Set/Map查不到了。解决方法:尽量用不可变数据结构,或改用ID字段做比较。
继承漏了props:看似不报错,逻辑却不对。养成习惯:每写子类先写props,把...super.props先写好再加自己字段。
toString可读性不好:Equatable自动生成格式为ClassName(field1, field2, ...),字段多时挤在一起。开发阶段自己覆盖toString只输出关键字段。
props列表别放null:若字段类型是非空但给了null,会运行时异常。建议props返回类型写List<Object?>(带问号)。
注意事项
props只放用于判断相等的字段,如name、age等;不应放时间戳、自增ID、缓存数据这些每个实例不同的字段。性能不是问题,一万个对象十万次比较Equatable比手动写==慢不到5%。和Freezed的比较:Equatable运行时比较,简单直接;Freezed编译期代码生成,功能更全。简单模型用Equatable即可,复杂场景可混用。
在HarmonyOS上运行Flutter项目,安装equatable只需一行依赖:equatable: ^2.0.7,然后flutter pub get。没有额外配置,纯Dart依赖,在鸿蒙上一样可用。demo代码在lib/demos/equatable_demo.dart,覆盖手工==、Equatable、继承、Mixin、空props、Set/Map用法、注意事项等内容。验证环境:Flutter 3.x + HarmonyOS 4.x(DevEco Studio模拟器),Dart SDK 3.x。
用了Equatable后,每个数据模型平均省七八行代码,二十多个模型加起来省两百行样板代码。每次少写一次==就少一个潜在bug点。如果你用过Java的Lombok或Kotlin的data class,会觉得Equatable思路特别亲切。 |