Java에서 Enum

Enum은 Java 1.5에서 도입된 새로운 타입으로, 필드는 고정된 상수 집합으로 구성됩니다. 예를 들어, 우리는 동, 서, 남, 북과 같은 고정된 필드를 가진 Java Enum으로 방향을 생성할 수 있습니다.

Java Enum

이 튜토리얼에서는 Enum을 생성하는 방법을 배우게 될 것입니다. 또한 Java에서 Enum을 사용하는 이점과 Enum 타입의 특징에 대해 알아볼 것입니다. 또한 Java Enum의 valueOf, enum values, EnumSetEnumMap을 예제와 함께 사용하는 방법도 배우게 될 것입니다.

Java Enum 예제

Java enum 키워드는 enum 타입을 생성하는 데 사용됩니다. Java enum 예제 프로그램을 살펴보겠습니다.

package com.journaldev.enums;

public enum ThreadStates {
	START,
	RUNNING,
	WAITING,
	DEAD;
}

위의 예제에서 ThreadStates는 START, RUNNING, WAITING 및 DEAD와 같은 고정된 상수 필드를 가진 enum입니다.

Java Enum vs 상수

이제 Java 클래스의 일반적인 상수 필드보다 Java enum이 더 우수한지 살펴보겠습니다. 비슷한 상수 클래스를 Java에서 생성해보겠습니다.

package com.journaldev.enums;

public class ThreadStatesConstant {
	public static final int START = 1;
	public static final int WAITING = 2;
	public static final int RUNNING = 3;
	public static final int DEAD = 4;
}

이제 자바 프로그램에서 enum 및 상수가 어떻게 사용되는지 살펴보겠습니다.

/**
* This method shows the benefit of using Enum over Constants
*/
private static void benefitsOfEnumOverConstants() {
	//Enum 값은 고정됩니다
	simpleEnumExample(ThreadStates.START);
	simpleEnumExample(ThreadStates.WAITING);
	simpleEnumExample(ThreadStates.RUNNING);
	simpleEnumExample(ThreadStates.DEAD);
	simpleEnumExample(null);
		
	simpleConstantsExample(1);
	simpleConstantsExample(2);
	simpleConstantsExample(3);
	simpleConstantsExample(4);
	//우리는 임의의 int 상수를 전달할 수 있습니다
	simpleConstantsExample(5);
}

private static void simpleEnumExample(ThreadStates th) {
	if(th == ThreadStates.START) System.out.println("Thread started");
	else if (th == ThreadStates.WAITING) System.out.println("Thread is waiting");
	else if (th == ThreadStates.RUNNING) System.out.println("Thread is running");
	else System.out.println("Thread is dead");
}
	
private static void simpleConstantsExample(int i) {
	if(i == ThreadStatesConstant.START) System.out.println("Thread started");
	else if (i == ThreadStatesConstant.WAITING) System.out.println("Thread is waiting");
	else if (i == ThreadStatesConstant.RUNNING) System.out.println("Thread is running");
	else System.out.println("Thread is dead");
}

위의 예제를 살펴보면, 상수 사용에 따른 두 가지 위험이 있으며 이는 enum으로 해결됩니다.

  1. 우리는 simpleConstantsExample 메서드에 임의의 int 상수를 전달할 수 있지만 simpleEnumExample에는 고정된 값만 전달할 수 있으므로 이는 형식 안정성을 제공합니다.
  2. ThreadStatesConstant 클래스에서 int 상수 값을 변경할 수 있지만 위의 프로그램은 예외를 throw하지 않습니다. 우리의 프로그램은 예상대로 작동하지 않을 수 있지만 enum 상수를 변경하면 컴파일 시간 오류가 발생하여 런타임 문제의 가능성을 제거합니다.

자바 Enum 메서드

이제 예제와 함께 자바 enum의 더 많은 기능을 살펴보겠습니다.

package com.journaldev.enums;

import java.io.Closeable;
import java.io.IOException;

/**
 * This Enum example shows all the things we can do with Enum types
 *
 */
public enum ThreadStatesEnum implements Closeable{
	START(1){
		@Override
		public String toString(){
			return "START implementation. Priority="+getPriority();
		}

		@Override
		public String getDetail() {
			return "START";
		}
	},
	RUNNING(2){
		@Override
		public String getDetail() {
			return "RUNNING";
		}
	},
	WAITING(3){
		@Override
		public String getDetail() {
			return "WAITING";
		}
	},
	DEAD(4){
		@Override
		public String getDetail() {
			return "DEAD";
		}
	};
	
	private int priority;
	
	public abstract String getDetail();
	//Enum 생성자는 항상 private여야 합니다.
	private ThreadStatesEnum(int i){
		priority = i;
	}
	
	//Enum에는 메서드를 가질 수 있습니다
	public int getPriority(){
		return this.priority;
	}
	
	public void setPriority(int p){
		this.priority = p;
	}
	
	//Enum은 함수를 오버라이드할 수 있습니다
	@Override
	public String toString(){
		return "Default ThreadStatesConstructors implementation. Priority="+getPriority();
	}

	@Override
	public void close() throws IOException {
		System.out.println("Close of Enum");
	}
}

자바 Enum 중요 포인트

자바에서 Enums의 중요한 몇 가지 포인트는 다음과 같습니다.

  1. 모든 Java enum은 암시적으로 java.lang.Enum 클래스를 확장하며, 이 클래스는 Object 클래스를 확장하고 SerializableComparable 인터페이스를 구현합니다. 따라서 enum에서는 어떤 클래스도 확장할 수 없습니다.
  2. enum은 키워드이므로 패키지 이름을 이로 끝낼 수 없습니다. 예를 들어 com.journaldev.enum은(는) 유효한 패키지 이름이 아닙니다.
  3. Enum은 인터페이스를 구현할 수 있습니다. 위의 enum 예제에서는 Closeable 인터페이스를 구현하고 있습니다.
  4. Enum 생성자는 항상 private입니다.
  5. new 연산자를 사용하여 enum의 인스턴스를 생성할 수 없습니다.
  6. Java enum에서 추상 메서드를 선언할 수 있으며, 그러면 모든 enum 필드가 추상 메서드를 구현해야 합니다. 위의 예에서 getDetail()는 추상 메서드이며 모든 enum 필드가 이를 구현했습니다.
  7. Enum에 메서드를 정의할 수 있으며 enum 필드는 이를 재정의할 수 있습니다. 예를 들어 toString() 메서드가 enum에서 정의되어 있고 enum 필드 START가 이를 재정의했습니다.
  8. Java enum 필드에는 네임스페이스가 있으며, ThreadStates.START와 같이 클래스 이름으로만 enum 필드를 사용할 수 있습니다.
  9. Enums는 switch 문에서 사용할 수 있으며, 이 튜토리얼의 나중 부분에서 실제 사용 사례를 살펴볼 것입니다.
  10. 우리는 기존 기능을 손상시키지 않고 기존 열거형을 확장할 수 있습니다. 예를 들어, ThreadStates 열거형에 새로운 필드 NEW를 추가할 수 있습니다.
  11. 열거형 필드는 상수이므로 Java에서는 블록 문자와 밑줄을 사용하여 작성하는 것이 좋은 관례입니다. 예를 들어, EAST, WEST, EAST_DIRECTION 등입니다.
  12. 열거형 상수는 암시적으로 static과 final입니다.
  13. 열거형 상수는 final이지만 해당 변수를 변경할 수 있습니다. 예를 들어, setPriority() 메서드를 사용하여 열거형 상수의 우선 순위를 변경할 수 있습니다. 아래 예제에서 사용 예를 살펴보겠습니다.
  14. 열거형 상수가 final이므로 “==” 및 equals() 메서드를 사용하여 안전하게 비교할 수 있습니다. 두 가지 방법 모두 같은 결과를 갖습니다.

Java EnumSet, EnumMap, valueOf()

이제 열거형의 대부분의 기능을 알았으니 Java Enum 예제 프로그램을 살펴보겠습니다. 그런 다음 열거형의 몇 가지 기능을 더 배우겠습니다.

package com.journaldev.enums;

import java.io.IOException;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Set;

public class JavaEnumExamples {

	public static void main(String[] args) throws IOException {
				
		usingEnumMethods();
		
		usingEnumValueOf();
		
		usingEnumValues();
		
		usingEnumInSwitch(ThreadStatesEnum.START);
		usingEnumInSwitch(ThreadStatesEnum.DEAD);
		
		usingEnumMap();
		
		usingEnumSet();
		
	}

	private static void usingEnumSet() {
		EnumSet enumSet = EnumSet.allOf(ThreadStatesEnum.class);
		for(ThreadStatesEnum tsenum : enumSet){
			System.out.println("Using EnumSet, priority = "+tsenum.getPriority());
		}
	}

	private static void usingEnumMap() {
		EnumMap<ThreadStatesEnum, String> enumMap = new EnumMap<ThreadStatesEnum,String>(ThreadStatesEnum.class);
		enumMap.put(ThreadStatesEnum.START, "Thread is started");
		enumMap.put(ThreadStatesEnum.RUNNING, "Thread is running");
		enumMap.put(ThreadStatesEnum.WAITING, "Thread is waiting");
		enumMap.put(ThreadStatesEnum.DEAD, "Thread is dead");
		
		Set keySet = enumMap.keySet();
		for(ThreadStatesEnum key : keySet){
			System.out.println("key="+key.toString()+":: value="+enumMap.get(key));
		}
		
	}

	private static void usingEnumInSwitch(ThreadStatesEnum th) {
		switch (th){
		case START:
			System.out.println("START thread");
			break;
		case WAITING:
			System.out.println("WAITING thread");
			break;
		case RUNNING:
			System.out.println("RUNNING thread");
			break;
		case DEAD:
			System.out.println("DEAD thread");
		}
	}

	private static void usingEnumValues() {
		ThreadStatesEnum[] thArray = ThreadStatesEnum.values();
		
		for(ThreadStatesEnum th : thArray){
			System.out.println(th.toString() + "::priority="+th.getPriority());
		}
	}

	private static void usingEnumValueOf() {
		ThreadStatesEnum th = Enum.valueOf(ThreadStatesEnum.class, "START");
		System.out.println("th priority="+th.getPriority());
	}

	private static void usingEnumMethods() throws IOException {
		ThreadStatesEnum thc = ThreadStatesEnum.DEAD;
		System.out.println("priority is:"+thc.getPriority());
		
		thc = ThreadStatesEnum.DEAD;
		System.out.println("Using overriden method."+thc.toString());
		
		thc = ThreadStatesEnum.START;
		System.out.println("Using overriden method."+thc.toString());
		thc.setPriority(10);
		System.out.println("Enum Constant variable changed priority value="+thc.getPriority());
		thc.close();
	}

}

열거형의 다른 중요한 기능을 설명하기 전에 위의 프로그램의 출력을 살펴보겠습니다.

priority is:4
Using overriden method.Default ThreadStatesConstructors implementation. Priority=4
Using overriden method.START implementation. Priority=1
Enum Constant variable changed priority value=10
Close of Enum
th priority=10
START implementation. Priority=10::priority=10
Default ThreadStatesConstructors implementation. Priority=2::priority=2
Default ThreadStatesConstructors implementation. Priority=3::priority=3
Default ThreadStatesConstructors implementation. Priority=4::priority=4
START thread
DEAD thread
key=START:: value=Thread is started
key=RUNNING:: value=Thread is running
key=WAITING:: value=Thread is waiting
key=DEAD:: value=Thread is dead
Using EnumSet, priority = 10
Using EnumSet, priority = 2
Using EnumSet, priority = 3
Using EnumSet, priority = 4

중요한 점

  1. usingEnumMethods() 메서드는 열거형 객체를 생성하는 방법과 해당 메서드를 사용하는 방법을 보여줍니다. 또한 열거형 변수를 변경하기 위한 setPriority(int i) 메서드의 사용법을 보여줍니다.
  2. usingEnumValueOf()는 문자열에서 열거형 객체를 생성하는 방법을 보여줍니다. 이를 위해 java.util.EnumvalueOf(enumType, name)을 사용합니다. 지정된 열거형 유형에 지정된 이름을 가진 상수가 없거나 지정된 클래스 객체가 열거형 유형을 나타내지 않는 경우 IllegalArgumentException을 throw합니다. 또한 인수 중 하나가 null인 경우 NullPointerException을 throw합니다.
  3. usingEnumValues() 메서드는 열거형의 모든 값을 포함하는 배열을 반환하는 values() 메서드의 사용법을 보여줍니다. 이 메서드는 모든 열거형에 대해 자동으로 생성되며 java.util.Enum 클래스에는 values() 구현이 없습니다.
  4. usingEnumInSwitch() 메서드는 switch case에서 열거형 상수를 사용하는 방법을 보여줍니다.
  5. usingEnumMap() 메서드는 Java 1.5 Collections Framework에서 소개된 java.util.EnumMap의 사용법을 보여줍니다. EnumMap은 열거형 키와 함께 사용하는 Map 구현체입니다. 열거형 맵의 모든 키는 맵이 생성될 때 명시적 또는 암시적으로 지정된 단일 열거형 유형에서 가져와야 합니다. EnumMap에는 키로 null을 사용할 수 없으며 EnumMap은 동기화되지 않습니다.
  6. usingEnumSet() 메서드는 java.util.EnumSet을 사용하여 enum 유형과 함께 사용하는 Set 구현을 보여줍니다. Enum set의 모든 요소는 집합이 생성될 때 명시적 또는 암시적으로 지정된 단일 enum 유형에서 가져와야 합니다. EnumSet은 동기화되지 않으며 null 요소는 허용되지 않습니다. 또한 copyOf(Collection<E> c), of(E first, E... rest), complementOf(EnumSet<E> s)와 같은 유용한 메서드를 제공합니다.

모든 예제는 GitHub 저장소에서 확인할 수 있습니다.

참고: Oracle 문서

Source:
https://www.digitalocean.com/community/tutorials/java-enum