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

异构列表中函数类型不变性的Dart解决方案

解决Dart中泛型FilterOption列表的类型不兼容问题

问题描述

编写如下Dart代码后,运行时出现类型不兼容错误:

void main() {
  final filters = [
    FilterOption(
      label: 'Category',
      hint: 'All categories',
      items: A.values,
      onChange: (_) => print('Not implemented yet'),
      itemLabeler: (item) => item.toString().toUpperCase()
    ),
    FilterOption(
      label: 'Time',
      hint: 'All time',
      items: B.values,
      onChange: (_) => print('Not implemented yet'),
      itemLabeler: (item) => item.toString().toLowerCase()
    )
  ];
  try {
    filters.first.itemLabeler;
  } catch(e) {
    print(e);
  }
  print('Program terminated');
}

enum A {
  here, are, some, options
}

enum B {
  here, are, another, few, options
}

class FilterOption<T> {
  final String label;
  final String hint;
  final List<T> items;
  final void Function(T item) onChange;
  final String Function(T item) itemLabeler;

  const FilterOption({
    required this.label,
    required this.hint,
    required this.items,
    required this.onChange,
    required this.itemLabeler
  });
}

运行输出:

TypeError: Instance of '(A) => String': type '(A) => String' is not a subtype of type '(_Enum) => String'
Program terminated

需求:支持将任意数量、任意泛型类型(不限于枚举)的FilterOption传入UI组件展示。

解决方案

1. 显式指定列表的通配泛型类型

创建列表时,明确将其类型声明为List<FilterOption<Object>>或List<FilterOption<dynamic>>,同时显式指定每个FilterOption的泛型参数,避免Dart自动推断出错误的公共父类型:

void main() {
  final filters = <FilterOption<Object>>[
    FilterOption<A>(
      label: 'Category',
      hint: 'All categories',
      items: A.values,
      onChange: (_) => print('Not implemented yet'),
      itemLabeler: (item) => item.toString().toUpperCase()
    ),
    FilterOption<B>(
      label: 'Time',
      hint: 'All time',
      items: B.values,
      onChange: (_) => print('Not implemented yet'),
      itemLabeler: (item) => item.toString().toLowerCase()
    )
  ];
  // 后续操作不再触发类型错误
  print(filters.first.itemLabeler);
  print('Program terminated');
}

2. 抽象无泛型基类统一接口

创建一个不包含泛型的抽象基类,定义UI展示所需的核心方法,让FilterOption<T>实现该基类。这样列表可以声明为List<BaseFilterOption>,既保证UI层的统一访问,又保留业务层的泛型类型安全:

abstract class BaseFilterOption {
  String get label;
  String get hint;
  List<Object> get items;
  String getLabelForItem(Object item);
}

class FilterOption<T> implements BaseFilterOption {
  @override
  final String label;
  @override
  final String hint;
  final List<T> items;
  final void Function(T item) onChange;
  final String Function(T item) itemLabeler;

  const FilterOption({
    required this.label,
    required this.hint,
    required this.items,
    required this.onChange,
    required this.itemLabeler
  });

  @override
  List<Object> get items => List.unmodifiable(this.items);

  @override
  String getLabelForItem(Object item) {
    return itemLabeler(item as T);
  }
}

// 使用示例
void main() {
  final filters = <BaseFilterOption>[
    FilterOption<A>(
      label: 'Category',
      hint: 'All categories',
      items: A.values,
      onChange: (_) => print('Not implemented yet'),
      itemLabeler: (item) => item.toString().toUpperCase()
    ),
    FilterOption<B>(
      label: 'Time',
      hint: 'All time',
      items: B.values,
      onChange: (_) => print('Not implemented yet'),
      itemLabeler: (item) => item.toString().toLowerCase()
    )
  ];
  print(filters.first.getLabelForItem(A.here));
  print('Program terminated');
}

3. 类型擦除包装类(快速实现方案)

如果对编译时类型安全要求不高,可以创建一个包装类,通过dynamic擦除内部FilterOption<T>的泛型类型,实现统一列表管理:

class FilterOptionWrapper {
  final dynamic _inner;

  const FilterOptionWrapper(this._inner);

  String get label => _inner.label;
  String get hint => _inner.hint;
  List get items => _inner.items;
  String getLabelForItem(dynamic item) => _inner.itemLabeler(item);
  void onChange(dynamic item) => _inner.onChange(item);
}

// 使用示例
void main() {
  final filters = [
    FilterOptionWrapper(FilterOption<A>(
      label: 'Category',
      hint: 'All categories',
      items: A.values,
      onChange: (_) => print('Not implemented yet'),
      itemLabeler: (item) => item.toString().toUpperCase()
    )),
    FilterOptionWrapper(FilterOption<B>(
      label: 'Time',
      hint: 'All time',
      items: B.values,
      onChange: (_) => print('Not implemented yet'),
      itemLabeler: (item) => item.toString().toLowerCase()
    ))
  ];
  print(filters.first.getLabelForItem(B.another));
  print('Program terminated');
}

方案选择建议

  • 优先选择抽象基类方案:兼顾类型安全与UI层统一访问,适合大多数生产场景。
  • 快速原型开发可选择显式通配泛型类型:实现简单,代码改动量小。
  • 类型擦除包装类仅适合对类型安全要求极低的临时场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 06:13:28