티스토리 뷰

Java/개념정리

[Java] enum(열거형)

통통푸딩 2021. 10. 14. 23:13
반응형

enum(열거형)이란, 서로 관련된 상수를 편하게 선언하기 위한 것으로 상수를 여러개 정의해야 할 때 사용한다.

즉, 서로 연관된 상수들의 집합이다.

 

수많은 언어는 사용자들이 새로운 열거형을 정의할 수 있게 하고 있다.

C언어에서는 enum 클래스의 역할은 단순히 상수들의 집합으로 사용되었지만,

자바에서는 타입까지 비교가 가능하다.

 

1. enum 정의하는 방법 📝

enum 은 {} 안에 상수의 이름을 나열하기만 하면 된다.

//enum 열거형 이름 { 상수명1, 상수명2, 상수명3 ... 상수명 n }
public class EnumExample {
  enum Language {
    ENGLISH, KOREAN, CHINESE, JAPANESE
  }

  public static void main(String[] args) {
    System.out.println(Language.ENGLISH.ordinal());
    System.out.println(Language.KOREAN.ordinal());
    System.out.println(Language.CHINESE.ordinal());
    System.out.println(Language.JAPANESE.ordinal());
  }
}

/* 실행 결과

0
1
2
3

*/

위와 같이 만들면 왼쪽부터 0번으로 숫자가 측정된다.

열거형에 정의된 상수를 사용하는 방법은 열거형이름.상수명 이다.

 

 

Enum의 장점

Enum은 코드의 가독성을 높이고 논리적인 오류를 줄일 수 있는 장점이 있다.

 

1) 상수와 리터럴이 논리적인 연관이 없는 문제를 해결할 수 있다.

public static final int ENGLISH = 1;
public static final int KOREAN = 2;
public static final int CHINESE = 3;

ENGLISH라는 상수와 부여된 1이라는 리터럴은 논리적으로 아무 연관이 없다.

굳이 1이어야 하는 이유도 없다.

 

 

2) 서로 다른 개념끼리 이름이 충돌할 수 있는 문제를 해결할 수 있다.

예를 들어서 과일의 APPLE과 회사의 APPLE은 같은 APPLE임에도 완전히 다른 의미의 단어이다.

Fruit.APPLE과 Company.APPLE같이 열거형으로 정의할 경우 완전히 구분히 가능하다. 즉, 문맥을 담을 수 있다.

 

 

3) 리팩토링 변경 범위가 최소화된다.

내용의 추가가 필요하더라도, enum 코드 외에 수정할 필요가 없다.

 

 

Java Enum의 특징

1) enum에 정의된 상수들은 enum 타입의 객체이다.

C언어나 다른 언어들과 달리 자바의 enum은 단순 정수 값이 아니라 해당 enum 타입 객체이다.

 

 

2) 생성자와 메서드를 추가할 수 있다.

자바에서 enum은 클래스라고 볼 수 있다.

열거형을 선언할 때 상수의 이름만 선언하는게 아닐 때는 마지막에 세미콜론을 붙여야 한다.

 

자바에서 enum은 생성자를 만들 수 있다.

이 때 생성자의 접근제어자는 private 라서 외부에서 접근할 수 없다.

열거된 멤버 중 하나를 호출하면, 열거된 모든 상수의 객체가 생성된다.

상수 하나당 각각의 객체가 만들어지고 모두 public static final 이다.

enum Language {
    ENGLISH, KOREAN, CHINESE, JAPANESE;  //열거할 상수 선언

    Language () {  //private임.
        System.out.println("생성자 호출 : " + this.name());
    }
}

public class EnumExample {

    public static void main(String[] args) {
        Language korean = Language.KOREAN;

            //Language korean = new Language();
            //열거형의 생성자의 접근제어자는 항상 private -> 에러 발생.
    }
}

/* 실행 결과

생성자 호출 : ENGLISH
생성자 호출 : KOREAN
생성자 호출 : CHINESE
생성자 호출 : JAPANESE

*/

 

 

생성자를 이용해서 상수에 데이터를 추가할 수 있다.

enum Language {
    ENGLISH(100), KOREAN(20), CHINESE(-10), JAPANESE(5);  //열거할 상수 선언

        private int value;

    Language (int value) {  //private임.
        this.value = value;
    }

        public int getValue() {  //메서드 추가
            return value;
        }
}

public class EnumExample {

    public static void main(String[] args) {
        System.out.println(Language.ENGLISH.value());  //100
    }
}

 

 

추상메서드를 선언하여 각 상수 별로 다르게 동작하도록 코드를 작성할 수도 있다.

enum은 각 클래스를 인스턴스화 시킨것이기 때문에 오버라이딩이 된다.

enum Transport{
  TAXI(4000){
    @Override  
    double calFare(int distance){  //오버라이딩
      return distance * BASIC_FARE * 3.0;
    }
  },

  BUS(1100){
    @Override
    double calFare(int distance){
      return distance * BASIC_FARE * 2.0;
    }
  },

  AIRPLANE(50000){
    @Override
    double calFare(int distance){
      return distance * BASIC_FARE * 10.0;
    }
  };

  //기본 요금, protected 이어서 상수에서 접근 가능
  protected final int BASIC_FARE;

  Transport (int basicFare) {  //private임.
    this.BASIC_FARE = basicFare;
  }

  abstract double calFare(int distance);  //추상 메서드 선언
}

 

 

 

3) enum 상수 간의 비교가 가능하다.

enum 상수 간에 == 를 통해 비교를 할 수 있다.

그러나 >, <와 같은 비교연산자는 사용할 수 없고 compareTo()를 사용할 수 있다.

Language lang;

if(lang == Language.KOREAN) {
    // ...
}

if(lang.compareTo(Language.KOREAN) > 0) {
    // ...
}

 

 

 

4) enum은 switch문의 조건식에도 들어갈 수 있다.

사용자 정의 타입과 다르게 enum은 switch문의 조건식에도 사용할 수 있다.

switch(lang){
    case ENGLISH :
        name = "영어";
        break;
    case KOREAN :
        name = "한국어";
        break;
    case CHINESE:
        name = "중국어";
        break;    
    case JAPANESE :
        name = "일본어";
        break;
}




2. Enum이 제공하는 메서드 📝

values()를 제외한 아래의 메서드들은 모두 java.lang.Enum 클래스에 정의된 메서드이다.

 

1) values()

이 메서드는 해당 enum 타입에 정의된 상수 배열을 반환한다.

즉, enum의 모든 상수들을 배열로 만들어준다.

이 메서드는 모든 열거형이 가지고 있는 것으로 컴파일러가 자동으로 추가해준다.

//T[] values()

Language[] languages = Language.values();

for(Language lang : languages){
    System.out.println(lang);
}

/* 실행 결과

ENGLISH
KOREAN
CHINESE
JAPANESE

*/

 

 

2) valueOf()

지정된 열거형에서 name과 일치하는 열거형 상수를 반환한다.

// T valueOf(Class<T> enumType, String name)
// T valueOf(String name)

public static void main(String[] args) {
    // 두 가지 형태로 가능
    Language korean = Enum.valueOf(Language.class, "KOREAN");
    Language english = Language.valueOf("ENGLISH");
}

 

 

3) oridinal()

열거형 상수가 정의된 순서를 반환한다. (왼쪽부터 0으로 시작)

// int ordinal()

enum Language {
  ENGLISH, KOREAN, CHINESE, JAPANESE
}

public static void main(String[] args) {
  System.out.println(Language.ENGLISH.ordinal());  //0
  System.out.println(Language.KOREAN.ordinal());   //1
  System.out.println(Language.CHINESE.ordinal());  //2
  System.out.println(Language.JAPANESE.ordinal()); //3
}

 

4) name()

열거형 상수의 이름을 문자열로 반환한다.

 

 

5) getDeclaringClass()

열거형 상수의 열거형 타입에 해당하는 클래스 객체를 반환한다.




3. java.lang.Enum 📝

Enum 클래스는 모든 자바 언어 열거타입의 상위 클래스다.

모든 열거형은 Enum 클래스를 상속받기 때문에 enum type은 별도의 상속을 받을 수 없다.

public abstract class Enum<E extends Enum<E>>
        implements Constable, Comparable<E>, Serializable {

    private final String name;

    private final String name() {
        return name;
    }

    //......

}

위에서 언급했던 Eum의 메서드들은 모두 여기 정의되어 있다.

toString()을 제외한 대부분의 메서드들은 final로 선언되어 있어서 오버라이딩을 할 수 없다.




4. EnumSet 📝

EnumSet 은 Enum을 가지고 Set 자료구조를 만들 수 있도록 고안된 Set 인터페이스 구현체다.

HashSet과 비교했을 때 성능 상 더 좋기 때문에 열거형 데이터를 위한 Set이 필요할 때는 EnumSet을 사용하는 것이 좋다.

EnumSet의 상속구조 (출처:  https://www.geeksforgeeks.org/enumset-class-java/ )



 

EnumSet의 특징

1) EnumSet은 AbstractSet 클래스를 상속하고 Set 인터페이스를 구현한다.

 

2) 오직 열거형 상수만을 값으로 가질 수 있으며 모든 값은 같은 enum 타입이어야 한다.

 

3) null 요소를 삽입할 수 없다. (NullPointException 발생)

 

4) ordinal 값의 순서대로 요소가 저장된다.

 

5) 다른 Collection 구현과 마찬가지로 EnumSet은 동기화되지 않는다. 즉, thread-safe하지 않다.

여러 쓰레드가 동시에 EnumSet에 접근하고 적어도 하나의 쓰레드가 집합을 수정하는 경우 외부적으로 동기화 되어야한다. 이것은 일반적으로 EnumSet을 자연스럽게 캡슐화하는 일부 객체에서 동기화하여 수행된다.

이러한 개체가 없이 동기식으로 사용하려면 Collections.synchronizedSet(Set) 메서드를 사용하여 set을 래핑해야한다.

 

6) 모든 메서드는 arithmetic bitwise operation을 사용하기 때문에 모든 기본 연산의 시간 복잡도가 O(1)이다.

 

7) EnumSet의 내부는 추상(abstract) 클래스이기 때문에 객체로써 생성 및 사용이 불가능하다. 즉, new 연산자 사용이 불가하다.

 

 

EnumSet의 메서드

//매개변수로 받은 요소 타입을 사용하여 비어 있는 EnumSet 생성
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType)

//매개변수로 받은 타입의 모든 열거형 상수들을 포함하는 EnumSet 생성
public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType)

//매개변수로 받은 EnumSet과 동일한 타입을 사용하여 동일한 요소를 포함하는 EnumSet 생성
public static <E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s)

//매개변수로 받은 Collection으로 초기화 된 EnumSet 생성
public static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c)

//매개변수의 EnumSet에 포함되어있지 않은 요소들을 포함하는(차집합) EnumSet 생성
public static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s)

//매개변수로 받은 요소를 포함하는 열거형 집합 생성
public static <E extends Enum<E>> EnumSet<E> of(E e1)
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2)
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3)
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4)
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5)

//가변인자 기능을 사용하는 of메서드의 여섯번째 오버로딩.
@SafeVarargs
public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest)

//매개변수 from부터 to 범위의 모든 요소를 포함하는 EnumSet 생성(to까지 포함)
public static <E extneds Enum<E>> EnumSet<E> range(E from, E to)

 

 

위의 메서드들을 예제를 통해 살펴보자.

import java.util.EnumSet;

enum Day {
  MON, TUE, WEN, THU, FRI, SAT, SUN
}

public class EnumExample {

  public static void main(String[] args) {
    EnumSet<Day> set1, set2, set3, set4, set5;

    set1 = EnumSet.allOf(Day.class);
    set2 = EnumSet.of(Day.MON, Day.WEN, Day.FRI);
    set3 = EnumSet.complementOf(set2);
    set4 = EnumSet.range(Day.WEN, Day.SAT);

    set5 = EnumSet.noneOf(Day.class);
    set5.add(Day.SAT);
    set5.add(Day.SUN);
    set5.remove(Day.SAT);

    System.out.println("set1 = " + set1);
    System.out.println("set2 = " + set2);
    System.out.println("set3 = " + set3);
    System.out.println("set4 = " + set4);
    System.out.println("set5 = " + set5);
    System.out.println(set5.contains(Day.SAT));
  }
}

 

실행결과는 다음과 같다.

set1 = [MON, TUE, WEN, THU, FRI, SAT, SUN]
set2 = [MON, WEN, FRI]
set3 = [TUE, THU, SAT, SUN]
set4 = [WEN, THU, FRI, SAT]
set5 = [SUN]
false

 

 

 

자바의 Enum에 관한 좋은 글을 발견했다.

우아한형제들 기술블로그 글인데, 한번 읽어보는것을 추천합니당

https://techblog.woowahan.com/2527/

 

Java Enum 활용기 | 우아한형제들 기술블로그

{{item.name}} 안녕하세요? 우아한 형제들에서 결제/정산 시스템을 개발하고 있는 이동욱입니다. 이번 사내 블로그 포스팅 주제로 저는 Java Enum 활용 경험을 선택하였습니다. 이전에 개인 블로그에 E

techblog.woowahan.com

 

반응형
댓글
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday