4 분 소요

Enum

Enum은 열거형이라고 불리며, 서로 연관된 상수들의 집합을 의미한다. 데이터 중에는 성별(남,여) 과 같이 한정된 값을 갖는 경우가 있다. 이때 Enum을 사용한다. 성별과 같이 한정된 데이터만 갖는 타입을 열거타입(Enumeration Type) 이라고 하고 남,여 와 같은 값들을 열거상수(Enumeration constant) 라고 한다.

관례로 열거타입의 경우 자바 클래스처럼 첫글자는 대문자로 하고 열거상수는 전체를 대문자로 한다. 만약 열거상수가 2개의 단어로 연결되어 있을 때는 _로 연결한다.

Enum 구조

Java에서 열거상수는 상수 각각을 내부적으로 public static final 필드이면서 객체로 제공되도록 한다. static 이기 때문에 각 상수는 클래스로더가 로드 시점에 JVM Method(static) 영역에 클래스 변수를 항상 상주시켜서 프로그램이 종료되기 전까지 항상 사용할 수 있게 주소공간을 확보한다.

Gender gender = Gender.MALE;

Enum을 사용할때 클래스 인스턴스를 생성하는 것 과 비슷하지만 new가 없는 형태이다. 어떤 클래스에서든 위와 같은 형태로 사용할 경우 Heap영역에 Gender 객체는 MALE, FEMALE 이라는 각각 java.lang.Enum 클래스를 상속받는 고유의 객체가 만들어지고 JVM Method영역의 열거상수들은 해당 객체를 바라본다. 위와 같은 로직을 만날 경우 Method에 할당되었던 메모리에 Heap영역에 생긴 객체들이 할당된다.

gender 변수는 JVM Stack 영역에서 사용하므로 gender 는 Method영역에 있는 MALE의 객체의 주소 값을 복사하고 gender과 Gender.MALE 은 Heap에 생성된 같은 객체를 바라본다.

Gender gender1 = Gender.MALE;
Gender gender2 = Gender.MALE;
gender1 == gender2	//true

때문에 이 경우 gender1, gender2 는 모두 같은 객체의 주소를 할당하고 있어서 결과값이 true가 된다.

장점

  1. 코드가 단순해지며, 가독성이 좋아진다.
  2. 인스턴스 생성과 상속을 방지하여 상수 값의 타입안정성이 보장된다.
  3. enum class를 사용해서 타입을 정의함으로 지정된 enum 타입 이외의 데이터값을 넣는지 컴파일시 체크할 수 있다.
  4. 키워드 enum을 사용함으로 의도가 열거임을 분명하게 알 수 있다.

예시

public class EnumExample {
	// 기존에 상수를 정의하는 방법
	public static final String MALE = "MALE";
	public static final String FEMALE = "FEMALE";

	public static void main(String[] args) {
		String gender1;
		gender1 = EnumExample.MALE;
		gender1 = EnumExample.FEMALE;
		// MALE, FEMALE이 아닌 상수 값이 할당 될 때 //컴파일시 에러가 나지 않음. -> 문제점 발생.
		gender1 = "boy";
		Gender gender2;
		gender2 = Gender.MALE;
		gender2 = Gender.FEMALE;
		// 컴파일 시 의도하지 않는 상수 값을 체크할 수 있음.
		// Enum으로 정의한 상수값만 할당 받을 수 있음.
		gender2 = "boy";
	}
}

// enum class를 이용해 Gender라는 새로운 상수들의 타입을 정의한다.
enum Gender { 
	MALE,
	FEMAL; 
}
public static final String MALE = "MALE";
public static final String FEMALE = "FEMALE";

위와 같이 기존의 상수를 정의하는 방법대로 상수를 정의하고 String gender1 에 Gender.MALE, Gender.FEMALE 할당을 한다. 하지만 이 경우 gender1에 “boy” 와 같은 의도치 않은 값을 할당해도 문제없이 할당이 된다. 때문에 만약 개발자가 기대하는 상수값이 할당되었는지에 대한 유효성 검사 로직이 들어가 있지 않다면, 런타임시 문제가 발생할 수 있다.

하지만 Gender gender2 = Gender.MALE 처럼 열거타입으로 선언을 하고 Gender.MALE 과 같은 열거상수를 할당할 때는 문제가 없지만 gender2에 Gender 상수값이 아닌 다른 값을 넣게되면 컴파일 에러가 발생하여 개발자가 바로 알 수 있다.

사용

기본 사용

public enum Gender{
    MALE, FEMALe
}

열거형으로 선언된 순서에 따라 0부터 인덱스 값을 갖고 순차적으로 증가된다. 열거상수는 기본적으로 첫번째부터 0 에서 1씩 증가하며 설정된다.

생성자

public enum Gender{
    MALE, FEMALe;
        
    private Gender(){
        System.out.println("생성자 = " + this.toString);
    }
}

enum은 열거형을 의미하는 특별한 형태의 클래스라서 일반 클래스처럼 생성자가 있어야 한다. 생성자를 만들지 않아도 기본 생성자가 자동으로 생긴다. 생성자의 접근제어자는 private이 되야한다. public이나 protected 로 하게 되면 컴파일 에러가 발생한다.

enum은 고정 상수들의 집합으로 런타임이 아닌 컴파일 타임에 모든 값을 알고 있어야 한다. 즉, 다른 패키지나 클래스에서 enum 타입에 접근해서 동적으로 어떤 값을 정해줄 수 없다. 따라서 컴파일 시 타입 안정성이 보장된다(해당 enum 클래스 내에서도 new 키워드로 인스턴스 생성이 불가능하고 newInstance(), clone() 등의 사용도 불가능). 이 때문에 생성자의 접근제어자를 private 으로 설정해야 한다. 이렇게 되면 외부에서 접근 가능한 생성자가 없으므로 enum 타입은 실제적으로 final 과 다름이 없다. 클라이언트에서 enum 인스턴스를 생성할 수 없고 상속을 받을 수도 없으므로, 클라이언트 관점에서 보면 인스턴스는 없지만 선언된 enum 상수는 존재하는 셈이다.

enum을 위 코드와 같이 작성하고 다음 코드를 실행해보면 앞서 설명했던 것처럼 이 코드를 만날때 각각의 열거 상수 객체가 생성됨을 알 수 있다.

Gender gender = Gender.MALE

// 결과
생성자 = MALE
생성자 = FEMALE

일반 메서드

public enum Gender{
    MALE, FEMALE;

    Gender(){
        System.out.println("생성자 = " + this.toString());
    }

    public void genderInfo(){
        System.out.println("gender");
    }
}

Gender.MALE.genderInfo();

불규칙적인 값, 추가 속성 부여

기본적으로 열거상수는 0부터 1씩 증가하는 값을 갖게 되지만 개발자가 직접 값을 지정하거나 추가 속성을 부여 할 수 있다. 이 경우 부여될 속성, 생성자를 새로 만들어 줘야 한다.

public enum Gender{
    MALE(1,"남자"), FEMALE(2,"여자");
    
    private final int value;
    private final String str;

    private Gender(int value, String str) {
        this.value = value;
        this.str = str;
    }
}

메서드 재정의

public enum Gender{
    MALE(10,"남자"){
    	@Override
        public int calc(int a, int b) {
            return a+b;
        }
    }, FEMALE(20,"여자"){
        @Override
        public int calc(int a, int b) {
            return a-b;
        }
    };
    
    private final int value;
    private final String str;

    private Gender(int value, String str) {
        this.value = value;
        this.str = str;
    }
    
    public abstract int calc(int a, int b);
}

Gender.MALE.calc(5,10);

abstract 메서드를 통해 열거형 상수 안에 각 상수별로 특정 메서드를 재정의 하도록 할 수 있다.

java.lang.Enum 클래스

Enum 클래스는 모든 자바 열거체의 공통된 조상 클래스이다. Enum 클래스에는 열거체를 조작하기 위한 다양한 메서드가 포함되어 있다.

Enum 메서드

values() 메서드

values() 메소드는 해당 열거체의 모든 상수를 저장한 배열을 생성하여 반환한다.

public enum Gender{
    MALE, FEMALe
}

Gender arr[] = Gender.values();

valueOf(String str) 메서드

전달된 문자열과 일치하는 해당 열거체의 상수를 반환한다.

public enum Gender{
    MALE, FEMALe
}

Gender gender = Gender.valueOf("MALE");

ordinal() 메서드

해당 열거체 상수가 열거체 정의에서 정의된 순서(0부터 시작)를 반환한다. 이때 반환되는 값은 순서이지 상수 값 자체가 아니다.

public enum Gender{
    MALE, FEMALe
}

int index = Gender.MALE.ordinal();	//0
메소드 설명
static E values() 해당 열거체의 모든 상수를 저장한 배열을 생성하여 반환함.
static E valueOf(String name) 전달된 문자열과 일치하는 해당 열거체의 상수를 반환함.
protected void finalize() 해당 Enum 클래스가 final 메소드를 가질 수 없게 됨.
String name() 해당 열거체 상수의 이름을 반환함.
int ordinal() 해당 열거체 상수가 열거체 정의에서 정의된 순서(0부터 시작)를 반환함.

참고

https://www.nextree.co.kr/p11686/

https://honbabzone.com/java/java-enum/

https://limkydev.tistory.com/50

태그:

카테고리:

업데이트:

댓글남기기