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

Dart中如何定义CoreDetail子类需实现的抽象copyWith方法

实现方案

核心解决思路是使用F-bound递归泛型(自限定泛型),给基类增加一个指向子类实现类型的泛型参数,从类型层面约束copyWith方法的返回值匹配当前子类,调用时不需要额外类型强转。


第一步:改造CoreDetail抽象类

在原有泛型参数基础上,新增自限定泛型参数D extends CoreDetail<I, ID, D>,同时修正copyWith方法的两处问题:

  • 返回值声明为泛型D,对应子类自身类型
  • 修正item参数类型为绑定的泛型I,和类中定义的item字段类型匹配

改造后的基类完整代码:

@immutable
abstract class CoreDetail<I extends CoreItem<ID>, ID, D extends CoreDetail<I, ID, D>> {
  final I item;
  final double _qty;
  final double _discount;

  const CoreDetail({
    required this.item,
    double qty = 1,
    double discount = 0,
  })  : _discount = discount,
        _qty = qty;

  double get qty => _qty;

  double get discount => _discount;

  D copyWith({
    I? item,
    double? qty,
    double? discount,
  });
}

注意:当前写法下,方法参数传null代表「不更新该字段、保留原有值」,如果需要支持显式将某个字段赋值为null,需要额外定义通用的Value包装类区分空值语义,避免逻辑冲突。


第二步:子类实现示例

子类继承基类时,将自身类型传入基类的泛型D位置,重写copyWith方法时直接返回子类实例即可。
以下是可运行的完整示例:

// 原有代码依赖的CoreItem基类最小实现
@immutable
abstract class CoreItem<ID> {
  final ID id;
  const CoreItem({required this.id});
}

// 具体业务Item实现
class ProductItem extends CoreItem<int> {
  final String name;
  const ProductItem({required this.name, required int id}) : super(id: id);
}

// 具体业务Detail子类
class SaleDetail extends CoreDetail<ProductItem, int, SaleDetail> {
  const SaleDetail({
    required super.item,
    super.qty,
    super.discount,
  });

  @override
  SaleDetail copyWith({
    ProductItem? item,
    double? qty,
    double? discount,
  }) {
    return SaleDetail(
      item: item ?? this.item,
      qty: qty ?? this.qty,
      discount: discount ?? this.discount,
    );
  }
}

调用效果

调用子类copyWith方法时,返回值直接为子类类型,不需要做强制类型转换:

void main() {
  const detail = SaleDetail(
    item: ProductItem(name: "测试商品", id: 1),
    qty: 1,
    discount: 0
  );
  // updatedDetail静态类型直接为SaleDetail,无需as强转
  final updatedDetail = detail.copyWith(qty: 2, discount: 0.8);
  print(updatedDetail.qty); // 输出2
  print(updatedDetail.discount); // 输出0.8
}

备选方案(不修改基类泛型)

如果不想调整基类的泛型结构,可以直接在子类重写copyWith时使用Dart支持的协变返回类型特性,将返回值改为具体子类类型即可。但这种方式基类层面无法做统一约束,不同子类可能出现返回类型不统一的问题,仅适合子类数量少、逻辑简单的场景。

内容的提问来源于stack exchange,提问作者user2473015

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 23:21:43