Inor

[JAVA] enum (열거형의 다양한 사용법) 본문

Computer Engineering/Java

[JAVA] enum (열거형의 다양한 사용법)

Inor 2017. 8. 1. 16:09

enum(열거형)


 자바에서 상수를 표현할때 enum 키워드를 사용 합니다. enum이 자바에 추가되기 전에 사용하던 상수를 표현하는 몇 가지 방법이 있었습니다. 하지만, enum이 추가된 후에 대부분의 자바 개발자가 enum을 사용해서 상수를 표현할 정도로 중요한 기술 됐습니다. enum을 사용하면 상수라는 것을 명확히 표현할 수 있고 코드의 가독성이 증가 합니다. 자신만의 네임스페이스를 갖고 있으며 형 안정성을 보장 해줍니다. 또한, 원시 데이터 타입인 int와 char과 같이 switch 문에서 사용이 가능 합니다. 아래는 enum을 사용하는 방법들 입니다.


enum Currency{
	DOLLLAR, EURO, YEN, YUAN
}

public class EnumExam {

	public static void main(String[] args) {
		Currency curCurrency = Currency.EURO;
		switch(curCurrency){
		case DOLLLAR :
			System.out.println("달러 입니다.");
			break;
		case EURO :
			System.out.println("유로화 입니다.");
			break;
		}
	}
}


 Currnecy(통화)라는 enum을 선언하고 그것을 switch문에서 사용하는 예제를 확인 할 수 있습니다. 다음 예제는 enum에 특정 값을 추가하는 예제 입니다.


enum Currency{
	DOLLLAR(1100), EURO(1500), YEN(1000), YUAN(150);
	int value; 
	private Currency(int value){
		this.value = value;
	}
	public int getValue(){
		return value;
	}
}

public class EnumExam {

	public static void main(String[] args) {
		Currency curCurrency = Currency.EURO;
		switch(curCurrency){
		case DOLLLAR :
			System.out.println("달러의 환율은 " + curCurrency.getValue() + "원 입니다.");
			break;
		case EURO :
			System.out.println("유로화의 환율은 " + curCurrency.getValue() + "원 입니다.");
			break;
		}
	}
}


 enum인 Currency에 코드 몇 줄이 추가 된것을 확인할 수 있습니다. 자세히 살펴보면 클래스와 매우 유사한 구조를 갖고 있습니다. value라는 필드와 getter가 있고 생성자가 있습니다. DOLLAR(1100)은 매개변수가 1100인 생성자를 호출하고 있고 DOLLAR는 1100이라는 value를 갖고 있습니다. 그런데 여기서 특이한 점은 생성자가 private이라는 것 입니다. 사실 보통의 클래스를 설계할때 객체는 런타임 중에 생성이 되기 때문에 생성자는 외부에서 호출이 가능한 public으로 선언 합니다. 그러나 enum은 고정된 상수의 집합이기 때문에 컴파일 타임에 값을 알아야 합니다. 그렇기 때문에 생성자를 private으로 선언해서 외부의 클래스에서 생성자를 호출 할 수 없게 만들었습니다. 결과적으로 enum은 컴파일시에 정해지고 클라이언트에서 생성할 수 없기 때문에 타입 안정성이 보장 됩니다. 그래서 enum은 싱글톤 패턴을 구현하는데 사용되기도 합니다.




 enum의 확장


 enum에 필드와 생성자를 만들수 있다는 사실을 확인 했습니다. enum에 메서드를 만들어줄수 있는데 이것은 자바에만 있는 특징 입니다. C#에서 사용했던 열거형은 enum 값이 원시 데이터타 입과 유사하게 사용되기 때문에 내부에 필드를 만들거나 메서드를 만드는 것이 불가능 하지만 자바에서는 가능 합니다. 아래의 코드는  switch문 대신에 추상 메서드를 사용해서 새로운 enum 값이 추가 됐을때 enum의 확장성을 증가 시키고 개발자의 실수를 줄여줄 수 있는 예제 입니다.


enum Currency {
	DOLLLAR(1100){
		public void showCurrency(){
			System.out.println("달러의 환율은 " + getValue() + "원 입니다.");
		}
	}, 
	EURO(1500){
		public void showCurrency(){
			System.out.println("유로의 환율은 " + getValue() + "원 입니다.");
		}
	}, 
	YEN(1000){
		public void showCurrency(){
			System.out.println("엔화의 환율은 " + getValue() + "원 입니다.");
		}
	}, 
	YUAN(150){
		public void showCurrency(){
			System.out.println("위완화의 환율은 " + getValue() + "원 입니다.");
		}
	};
	
	private int value; 
	
	private Currency(int value){
		this.value = value;
	}
	
	public int getValue(){
		return value;
	}
	
	@Override
	public String toString(){
		String tempString = value+"원";
		return tempString;
	}
	
	public void showMe(){
		System.out.println("hello i'm");
	}
	
	/*
	 * 추상 메서드를 사용하면 상수에서 재정의가 가능합니다.
	 * enum의 확장성이 증가합니다.
	 * 개발자의 실수를 컴파일 시점에 확인 할 수 있습니다.
	 */
	public abstract void showCurrency();
}

public class EnumExam {
	public static void main(String[] args) {
		Currency myCurrency = Currency.EURO;
		myCurrency.showCurrency();
	}
}


 위의 예제에서 확인해야되는 부분은 public abstract void showCurrency() 입니다. 추상 메서드를 선언했고 각각의 enum 상수에 맞게 재정의를 해줬습니다. 위와 같은 방식으로 enum의 기능을 확장할 수 있습니다. 또한 switch문에서 사용했던 코드를 enum 상수 자체에서 사용하기 때문에 새로운 상수가 추가 됐을때 추상 메서드를 구현해야되게 때문에 switch문에서 수정하는 것보다 실수를 줄일 수 있을 것 같습니다. 실수를 줄일 수 있을것 같다는 것은 제 개인적인 생각 입니다. 또한 toString을 오버라이딩 할 수 있습니다.




enum의 상속


 enum은 기본적으로 상속이 불가능 합니다. 상속이 되는것 처럼 사용하려면 클래스를 extends하는 대신에 인터페이스를 implements 해서 상속을 흉내낼 수 있습니다. 아래는 상속을 흉내내는 코드 예제 입니다.


enum Currency implements Runnable{
	DOLLLAR(1100){
		public void showCurrency(){
			System.out.println("달러의 환율은 " + getValue() + "원 입니다.");
		}
		@Override
		public void run(){
			System.out.println("hello i'm run method in DOLLAR");
		}
	}, 
	EURO(1500){
		public void showCurrency(){
			System.out.println("유로의 환율은 " + getValue() + "원 입니다.");
		}
	}, 
	YEN(1000){
		public void showCurrency(){
			System.out.println("엔화의 환율은 " + getValue() + "원 입니다.");
		}
	}, 
	YUAN(150){
		public void showCurrency(){
			System.out.println("위완화의 환율은 " + getValue() + "원 입니다.");
		}
	};
	
	private int value; 
	
	private Currency(int value){
		this.value = value;
	}
	
	public int getValue(){
		return value;
	}
	
	@Override
	public String toString(){
		String tempString = value+"원";
		return tempString;
	}
	
	@Override
	public void run(){
		System.out.println("hello i'm run method");
	}
	
	/*
	 * 추상 메서드를 사용하면 상수에서 재정의가 가능합니다.
	 * enum의 확장성이 증가합니다.
	 * 개발자의 실수를 컴파일 시점에 확인 할 수 있습니다.
	 */
	public abstract void showCurrency();
}


 쓰레드로 사용할 수 있는 Runnable 인터페이스를 implements 했습니다. 그리고 run 메서드를 두 가지 방법으로 오버라이딩 했습니다. 첫번째는 Currency enum 자체에 해줬고 두번째는 DOLLAR라는 상수 내부에서 재정의를 했습니다. 첫번째 방법을 사용하면 run 메서드를 모든 상수에서 동일한 방법으로 사용할 수 있습니다. 하지만 상수마다 다른 동작을 하는 run 메서드를 만들고 싶다면 두번째 방법을 사용해주시면 됩니다. 두번째 방법으로 사용하는 경우의 코드는 아래와 같습니다.


enum Currency implements Runnable{
	 DOLLLAR(1100){
		public void showCurrency(){
			System.out.println("달러의 환율은 " + getValue() + "원 입니다.");
		}
		@Override
		public void run(){
			System.out.println("hello i'm run method in DOLLAR");
		}
	}, 
	EURO(1500){
		public void showCurrency(){
			System.out.println("유로의 환율은 " + getValue() + "원 입니다.");
		}
		@Override
		public void run(){
			System.out.println("hello i'm run method in EURO");
		}
	}, 
	YEN(1000){
		public void showCurrency(){
			System.out.println("엔화의 환율은 " + getValue() + "원 입니다.");
		}
		@Override
		public void run(){
			System.out.println("hello i'm run method in YEN");
		}
	}, 
	YUAN(150){
		public void showCurrency(){
			System.out.println("위완화의 환율은 " + getValue() + "원 입니다.");
		}
		@Override
		public void run(){
			System.out.println("hello i'm run method in YUAN");
		}
	};
}




참조


'Computer Engineering > Java' 카테고리의 다른 글

[Java] String (String constant pool)  (0) 2017.11.27
[Java] Generic (제네릭)  (0) 2017.11.26
[JAVA] 상수(Constant, enum)  (0) 2017.07.27
[JAVA] Runnable (Runnable Interface 사용법)  (0) 2017.07.24
[JAVA] Synchronized(동기화)  (0) 2017.07.20
Comments