[Java] Enum 알아보기

yerim·2025년 1월 29일

Java

목록 보기
4/5
post-thumbnail

서론


이번 주 멘토링 과제는 enum [whiteship:live-study] 11주차 과제: Enum 이다.
자바를 오랜만에 공부하다보니 관련 개념을 아예 잊어버리고 있었는데, 내부 코드를 찾아보면서 하나씩 찾아보았다.



0.enum 이란


Java enum제한된 값, 상수 값들의 목록의 가지는 타입을 의미한다. (enum 은 ‘Enumeration’ 의 약자로 열거, 목록이라는 뜻)

예전에는 특정 상수값을 사용하기 위해서 모두 상수로 선언했어야 한다. enum은 관련있는 상수 값들을 묶어 사용할 수 있도록 해준다. 대표적인 예로 요일, 계절, 제품 카테고리 등을 enum을 통해 사용할 수 있다.

장점

  1. 허용 가능한 값을 제한할 수 있다.
  2. 컴파일 시 데이터 타입 및 유효성 체크를 할 수 있다.
  3. 코드가 단순해지고 가독성 향상된다.
  4. 수정 시 enum만 수정하면 되므로 관리가 용이하다.
  5. 싱글톤 보장: 인스턴스의 생성과 상속을 방지하여 상수값의 안전성이 보장된다.


1. enum 정의하는 방법


enum 키워드를 통해 아래와 같이 정의한다. (열거 상수는 대문자로 작성한다.)

  1. 단순하게 정의하는 경우

    public enum Season {
    	SPRING,
    	SUMMER,
    	FALL,
    	WINTER
    }

    enum 에서 제공해주는 name() 메소드를 사용할 수도 있다. name() 은 호출된 값의 이름을 String으로 리턴한다.

    Season season1 = Season.FALL;
    System.out.println(season1); // 출력: FALL
    System.out.println(season1.name()); // 출력: FALL
  1. 각각의 요소들에 특정 값을 넣는 경우

    아래와 같이 생성자와 필드값을 추가할 수 있다. 단, new 키워드를 통해 인스턴스 생성은 불가하다.

    public enum Season {
    	SPRING("봄", 0),
    	SUMMER("여름", 1),
    	FALL("가을", 2),
    	WINTER("겨울", 3)
    	;
    	
    	private final String label;
    	private final String number;
    	
    	Season(String label, int number) {
    		this.label = label;
    		this.number = number;
    	}
    	
    	public String label() { return label; }
    	public int number() { return number; }
    }
    Season season1 = Season.FALL;
    System.out.println(season1.name()); // 출력: FALL
    System.out.println(season1.label()); // 출력: 가을 
    System.out.println(season1.number()); // 출력: 2 
  1. 클래스 안에 정의되는 경우

    public class User {
    	private Tier tier;
    	public enum Tier {
    		BRONZE,
    		SILVER,
    		GOLD
    	}
    }


2. enum이 제공하는 메소드 (values()와 valueOf())


values()

enum 의 요소들을 순서대로 enum 타입의 배열로 리턴한다.

Season.values();

valueOf()

매개변수로 주어진 String과 열거형 상수에서 일치하는 이름을 가진 원소를 반환한다. 일치하는 원소가 없는경우 IlleagalArgumentException 예외가 발생한다.

Season summer = Season.valueOf("SUMMER");


3. java.lang.Enum


enum은 class 키워드가 아닌 enum 키워드로 만들지만, enum 또한 클래스이고 object 클래스를 상속 받는다.

eunm 키워드를 통해 만들어진 모든 enum 타입은 모두java.lang.Enum클래스를 상속 받는다.

java.lang.Enum을 살펴보면

  • name : 열겨형 상수의 이름, toString() 이나 name() 을 통해서 접근할 수 있다.
  • ordinal : enum에서의 열거형 상수 선언 순서를 반환해준다. ordnal() 을 통해 접근할 수 있다. ordinal 값은 선언된 순서에 따라 변경되므로 해당 값은 사용하는 것은 좋지 않다. 보통 개발자들이 직접 사용하지는 않고, EnumSet이나 EnumMap과 같은 Enum기반 데이터 구조에서 사용한다.
  • 생성자 : enum 클래스를 정의하면 컴파일러가 추가하는 코드해서 호출하는 생성자로 상수마다 name과 ordinal 값을 설정한다.
  • equels() : 지정된 객체와 동일한지 판단한다. 여기서 내용이 아닌 == 을 통해 비교하는데, enum 상수는 인스턴스가 단 하나만 생성되기 때문이다. (싱글톤)
  • clone(): 사용하는 순간 바로 예외를 발생시킨다. 열거형 상수가 절대 복제되지 않음을 보장하는데, 싱글톤 상태를 유지하기 위함이다.
  • valueOf() : 이름으로 열거형 상수를 가져온다.
  • compareTo() : 열거형 상수의 순서를 비교한다. 순서가 낮은 경우 음수, 같은 경우 0, 큰 경우 양수 반환.


    💡모든 열거형 상수를 가져오는 values() 은 java.lang.Enum 에 정의되어 있지 않고, 컴파일러가 enum을 만들 때 추가해주는 메소드이다.



4. EnumSet


원소들이 enum 상수로만 이루어진 경우, enum을 위한 자료구조를 사용할 수 있는데 그 중 EnumSet이 있다. EnumSet은 Set 인터페이스를 구현하면서 AbstractSet을 상속하지만 대부분의 메서드를 재정의해서 사용한다.

특징

  • 열거형 값만 포함할 수 있고, 모든 값은 동일한 열거형에 속해야 한다.
  • null 값을 추가할 수 없고, 시도하는 경우 NullPointerException 이 발생한다.
  • 요소들은 열거형에 선언된 순서에 따라 저장된다.
  • thread 로부터 안전하지 않아, 필요한 경우 외부에서 동기화해야한다.
  • Eunmset의 모든 메서드는 산술 비트 연산자를 이용하여 구현한다.
  • 복사 시에 실패에 안전한 반복자를 사용하므로 컬렉션이 반복되는 도중에 변경되어도 ConcurrentModificationException 이 발생하지 않는다.

EnumSet을 쓰는 이유

빈 열거형 셋을 만들 때, 아래 메서드가 호출된다.

생성할 때, 열거형 상수의 갯수에 따라 RegularEnumSetJumboEnumSet으로 나뉘어 생성된다.

RegularEnumSet 은 long의 bit(64bit)를 기준으로 각 비트가 현재 열거형에서의 상수 위치를 반영하기 때문에 해당 값이 존재하는지 파악하는 것이 매우 빠르기 때문이다.

이렇게 구현되어 EnumSet의 모든 메서드는 산술 비트 연산을 사용해서 구현되어 있다.

보통 HashSet보다 빠르며 대량 작업도 일정한 시간에 수행된다. Set 값을 변경하는 게 아니라 조회를 할 경우 EnumSet을 사용하며 최적화할 수 있다.


EnumSet을 사용하는 방법

인스턴스를 생성하는 메서드를 제외하고, 대부분 Set 과 동일하게 동작한다.

public enum Tier {
		IRON,
		BRONZE,
		SILVER,
		GOLD
	}
// 모든 요소를 포함하는 EnumSet 생성한다.
EnumSet allTier = EnumSet.allOf(Tier.class);

//매개변수로 받는 열거형을 비운 EnumSet을 반환한다.
EnumSet.allOf(Tier.class);

// 매개변수로 받는 해당 요소를 찾아서 넣는다.
EnumSet.of(Tier.BRONZE, Tier.SILVER);

// 매개변수로 받는 요소들만 빼고 넣는다.
EnumSet.complementOf(Tier.BRONZE)

// 두 구간안에 해당하는 요소를 넣는다.
EnumSet.range(Tier.IRON, Tier.SILVER)

// add(), remove(), contains() 등 사용



profile
쌓아가는 중

0개의 댓글