Тип перечисления — это особый класс, предназначенный для представления фиксированного набора константных значений.
Все перечисления автоматически наследуют класс Enum. При этом перечисления относятся к запечатанным типам (sealed): их нельзя наследовать, реализовывать, добавлять как миксин, а также явно создавать экземпляры вручную.
Абстрактные классы и миксины могут явно реализовывать или наследовать 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;
// Геттер
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, возвращающее порядковый номер с нуля в объявлении перечисления:
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)
Доступ к пользовательским членам перечисления
В расширенных перечислениях к пользовательским полям, геттерам и методам можно обращаться как к обычным объектам:
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;
/// Геттер: принадлежит ли первому кварталу
bool get isFirstQuarter => monthNumber <= 3;
/// Геттер: имеет ли месяц 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 (индекс по порядку объявления, от нуля)
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)
Перечисления