枚举

枚举类型是一种特殊类,用于表示数量固定的一组常量值

所有枚举会自动继承 Enum 类。同时枚举属于密封类型(sealed):不能被继承、实现、混入,也无法显式手动实例化

抽象类和混入(mixin)可以显式实现或继承 Enum,但除非被枚举声明实现 / 混入,否则无法创建该类 / 混入的实际对象。

使用 enum 关键字,列出所有枚举常量即可定义简单枚举:

enum Color { red, green, blue }

声明枚举时允许末尾加逗号,便于复制粘贴、减少代码失误。

增强枚举(Enhanced enums)

Dart 支持增强枚举,可以像普通类一样定义成员字段、方法、常量构造函数,但实例只能是预先定义好的固定常量。

声明增强枚举有一系列强制约束:

  1. 实例变量必须是 final(包含混入引入的变量)
  2. 所有生成构造函数必须标记 const
  3. 工厂构造函数只能返回枚举内已定义的常量实例
  4. 不能再显式继承其他类(自动继承 Enum
  5. 不能重写 indexhashCode、相等运算符 ==
  6. 枚举内不能定义名为 values 的成员,和自动生成的静态 values 访问器冲突
  7. 所有枚举实例必须写在枚举定义最开头,且至少要有一个实例
  8. 增强枚举的实例方法中,可以使用 this 指代当前枚举常量

例子

enum Vehicle implements Comparable<Vehicle> {
  car(tires: 4, passengers: 5, carbonPerKilometer: 400),
  bus(tires: 6, passengers: 50, carbonPerKilometer: 800),
  bicycle(tires: 2, passengers: 1, carbonPerKilometer: 0);

  const Vehicle({
    required this.tires,
    required this.passengers,
    required this.carbonPerKilometer,
  });

  final int tires;
  final int passengers;
  final int carbonPerKilometer;

  // Getter
  int get carbonFootprint => (carbonPerKilometer / passengers).round();
  bool get isTwoWheeled => this == Vehicle.bicycle;

  @override
  int compareTo(Vehicle other) => carbonFootprint - other.carbonFootprint;
}Code language: Dart (dart)

Dart 3.13+ 支持主构造函数简化增强枚举写法:

enum Vehicle(
  final int tires,
  final int passengers,
  final int carbonPerKilometer,
) implements Comparable<Vehicle> {
  car(4, 5, 400),
  bus(6, 50, 800),
  bicycle(2, 1, 0);

  int get carbonFootprint => (carbonPerKilometer / passengers).round();
  bool get isTwoWheeled => this == Vehicle.bicycle;

  @override
  int compareTo(Vehicle other) => carbonFootprint - other.carbonFootprint;
}Code language: Dart (dart)

使用枚举

访问枚举常量和访问静态变量方式一致:

final favoriteColor = Color.blue;
if (favoriteColor == Color.blue) {
  print('Your favorite color is blue!');
}Code language: Dart (dart)

index 属性

每一个枚举常量自带 index,返回在枚举定义中从 0 开始的序号

assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);Code language: Dart (dart)

values 静态常量

通过 枚举名.values 获取包含所有枚举值的列表:

List<Color> colors = Color.values;
assert(colors[2] == Color.blue);Code language: Dart (dart)

switch 搭配枚举

switch 中使用枚举时,如果没有覆盖全部枚举分支,编译器会给出警告;可以使用 default 兜底

var aColor = Color.blue;

switch (aColor) {
  case Color.red:
    print('Red as roses!');
  case Color.green:
    print('Green as grass!');
  default:
    print(aColor); // Color.blue
}Code language: Dart (dart)

name 属性

通过 .name 获取枚举常量对应的字符串名称:

print(Color.blue.name); // 输出 "blue"Code language: Dart (dart)

访问枚举自定义成员

增强枚举可以像普通对象一样访问自定义字段、getter、方法:

print(Vehicle.car.carbonFootprint);Code language: CSS (css)

下面是本节课的一个完整例子,我们来做一个月份的枚举类,运用上面学到的知识点

enum Month implements Comparable<Month> {
  january(1, '一月', days: 31),
  february(2, '二月', days: 28),
  march(3, '三月', days: 31),
  april(4, '四月', days: 30),
  may(5, '五月', days: 31),
  june(6, '六月', days: 30),
  july(7, '七月', days: 31),
  august(8, '八月', days: 31),
  september(9, '九月', days: 30),
  october(10, '十月', days: 31),
  november(11, '十一月', days: 30),
  december(12, '十二月', days: 31);

  const Month(this.monthNumber, this.cnName, {required this.days});

  /// 公历月份数字 1~12
  final int monthNumber;

  /// 名称
  final String cnName;

  /// 平年天数
  final int days;

  /// Getter:是否是Q1季度
  bool get isFirstQuarter => monthNumber <= 3;

  /// Getter:是否31天
  bool get has31Days => days == 31;

  /// 自定义方法:判断二月是否闰年
  bool isLeapYearFebruary(int year) {
    if (this != Month.february) return false;
    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
  }

  /// 实现 Comparable,支持排序
  @override
  int compareTo(Month other) {
    return monthNumber - other.monthNumber;
  }
}

void main() {
  // 1. 基础访问枚举常量
  Month current = Month.august;
  print('当前枚举:$current');
  print('枚举name属性:${current.name}');
  print('月份:${current.cnName}');
  print('月份序号1~12:${current.monthNumber}');
  print('平年天数:${current.days}');
  print('是否31天月份:${current.has31Days}');
  print('是否第一季度:${current.isFirstQuarter}');

  // 2. index 属性(枚举定义顺序下标,从0开始)
  print('\n${Month.january.name} index = ${Month.january.index}');
  print('${Month.december.name} index = ${Month.december.index}');

  // 3. values 获取全部枚举列表
  List<Month> allMonths = Month.values;
  print('\n总共有 ${allMonths.length} 个月份');

  // 4. switch 使用枚举
  print('\n==== switch 判断 ====');
  switch (current) {
    case Month.january:
      print('一年之始');
    case Month.february:
      print('可能存在闰月');
    case Month.march:
    case Month.april:
    case Month.may:
      print('春季');
    case Month.june:
    case Month.july:
    case Month.august:
      print('夏季');
    case Month.september:
    case Month.october:
    case Month.november:
      print('秋季');
    case Month.december:
      print('冬季年末');
  }

  // 5. 调用自定义方法
  Month feb = Month.february;
  print('\n2024年是否闰二月:${feb.isLeapYearFebruary(2024)}');
  print('2025年是否闰二月:${feb.isLeapYearFebruary(2025)}');

  // 6. 枚举排序(依靠 Comparable 接口)
  var selected = [Month.october, Month.january, Month.july];
  selected.sort();
  print('\n排序后月份:${selected.map((m) => m.cnName).toList()}');
}Code language: Dart (dart)

执行结果

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
当前枚举:Month.august
枚举name属性:august
月份:八月
月份序号1~128
平年天数:31
是否31天月份:true
是否第一季度:false

january index = 0
december index = 11

总共有 12 个月份

==== switch 判断 ====
夏季

2024年是否闰二月:true
2025年是否闰二月:false

排序后月份:[一月, 七月, 十月]Code language: JavaScript (javascript)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注