枚举类型是一种特殊类,用于表示数量固定的一组常量值。
所有枚举会自动继承 Enum 类。同时枚举属于密封类型(sealed):不能被继承、实现、混入,也无法显式手动实例化。
抽象类和混入(mixin)可以显式实现或继承 Enum,但除非被枚举声明实现 / 混入,否则无法创建该类 / 混入的实际对象。
使用 enum 关键字,列出所有枚举常量即可定义简单枚举:
enum Color { red, green, blue }
声明枚举时允许末尾加逗号,便于复制粘贴、减少代码失误。
增强枚举(Enhanced enums)
Dart 支持增强枚举,可以像普通类一样定义成员字段、方法、常量构造函数,但实例只能是预先定义好的固定常量。
声明增强枚举有一系列强制约束:
- 实例变量必须是
final(包含混入引入的变量) - 所有生成构造函数必须标记
const - 工厂构造函数只能返回枚举内已定义的常量实例
- 不能再显式继承其他类(自动继承
Enum) - 不能重写
index、hashCode、相等运算符== - 枚举内不能定义名为
values的成员,和自动生成的静态values访问器冲突 - 所有枚举实例必须写在枚举定义最开头,且至少要有一个实例
- 增强枚举的实例方法中,可以使用
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~12:8
平年天数:31
是否31天月份:true
是否第一季度:false
january index = 0
december index = 11
总共有 12 个月份
==== switch 判断 ====
夏季
2024年是否闰二月:true
2025年是否闰二月:false
排序后月份:[一月, 七月, 十月]Code language: JavaScript (javascript)
Previous: Mixins
Next: Dot shorthands(点简写)