Перечисления

Тип перечисления — это особый класс, предназначенный для представления фиксированного набора константных значений.

Все перечисления автоматически наследуют класс Enum. При этом перечисления относятся к запечатанным типам (sealed): их нельзя наследовать, реализовывать, добавлять как миксин, а также явно создавать экземпляры вручную.

Абстрактные классы и миксины могут явно реализовывать или наследовать Enum, но создать реальный объект такого класса или миксина невозможно, если он не реализован или не подключен в объявлении перечисления.

Для определения простого перечисления используйте ключевое слово enum и перечислите все константы:

enum Color { red, green, blue }

При объявлении перечисления допускается запятая в конце списка значений — это упрощает копирование кода и снижает вероятность ошибок.

Расширенные перечисления (Enhanced enums)

В Dart поддерживаются расширенные перечисления. В них можно объявлять поля экземпляров, методы и константные конструкторы, как в обычных классах, но экземплярами могут быть только заранее заданные константы.

Объявление расширенного перечисления подчиняется ряду обязательных ограничений:

  1. Переменные экземпляра должны иметь модификатор final (в том числе переменные, добавленные миксином)
  2. Все обычные конструкторы помечаются модификатором const
  3. Фабричные конструкторы могут возвращать только заранее объявленные константы из этого перечисления
  4. Нельзя явно наследовать другие классы (наследование Enum происходит автоматически)
  5. Запрещено переопределять index, hashCode и оператор равенства ==
  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;

  // Геттер
  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~128
Дней в обычном году:31
Месяц с 31 днём?:true
Относится к первому кварталу?:false

january index = 0
december index = 11

Всего месяцев:12

==== Проверка через switch ====
Лето

2024 год — високосный февраль?:true
2025 год — високосный февраль?:false

Отсортированные месяцы:[一月, 七月, 十月]Code language: JavaScript (javascript)

Перечисления

Previous:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *