열거형

열거형은 특수한 클래스로, 개수가 고정된 상수 집합을 표현할 때 사용합니다.

모든 열거형은 자동으로 Enum 클래스를 상속받습니다. 또한 열거형은 봉인 타입(sealed)이므로 상속하거나 구현, 믹스인할 수 없고 명시적으로 직접 인스턴스를 생성할 수도 없습니다.

추상 클래스와 믹스인(mixin)은 명시적으로 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;

  // 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)

열거형 사용자 정의 멤버 접근

확장 열거형은 일반 객체처럼 사용자 정의 필드, 게터, 메서드에 접근할 수 있습니다.

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:1분기인지 확인
  bool get isFirstQuarter => monthNumber <= 3;

  /// Getter:31일인 달인지 확인
  bool get has31Days => days == 31;

  /// 사용자 정의 메서드:윤년 2월 판단
  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('1분기인지:${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년 윤2월 여부:${feb.isLeapYearFebruary(2024)}');
  print('2025년 윤2월 여부:${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
1분기인지:false

january index = 0
december index = 1112 개의 월

==== switch 판단 ====
여름

2024년 윤2월 여부:true
2025년 윤2월 여부:false

정렬 후 월:[一月, 七月, 十月]Code language: JavaScript (javascript)

열거형

Previous:

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다