Enum이란 Enumeration의 앞 글자로 열거라는 의미를 갖는다. 관련이 있는 상수들의 집합입니다. 자바에서는 final로 String과 같은 문자열이나 숫자들을 나타내는 기본 자료형의 값을 고정할 수 있습니다. 이렇게 고정된 값을 상수라고 합니다. 영어로는 constant입니다. 어떤 클래스가 상수만으로 작성되어 있으면 반드시 class로 선언할 필요는 없습니다. 이럴 때 class로 선언된 부분에 enum이라고 선언하면 이 객체는 상수의 집합이다. 라는 것을 명시적으로 나타냅니다.
기존에는 인터페이스나 클래스 내에서 상수를 선언함으로써 상수를 관리 하였는데 클래스 내에서 선언하는 부분은 네이밍이 겹칠 수 있고 불 필요하게 상수가 많아지는 단점이 있다.
인터페이스로 관리하는 경우 이런 부분은 줄어들지만 여전히 IDE의 지원을 적극적으로 받을 수 없고 타입 안정성이 떨어지는 단점을 가지고 있었다. 이를 보완하며 나온 것이 Enum이다.
private
로 되어 있으며 public
으로 변경하는 경우 컴파일 에러가 발생한다. 즉 다른 클래스나 인터페이스에서의 상수선언이 클래스 로드 시점에서 생성되는 것 처럼 Enum 또한 생성자가 존재하지만 클래스가 로드되는 시점에서 생성되기 때문에 임의로 생성하여 사용 할 수 없다. 이를 사용하고자 한다면 Rank.FIVE
와 같은 형태로 상수처럼 사용하면 된다. public enum Rank {
THREE(3, 4_000),
FOUR(4, 10_000),
FIVE(5, 30_000);
private final int match;
private final int money;
private int count;
Rank(int match, int money) { // Default 생성자는 private 으로 설정되어 있음.
this.match = match;
this.money = money;
}
싱글톤
형태로 어플리케이션 전체에서 사용된다. 싱글톤
으로 사용되기 때문에 각각의 Enum 인스턴스에 변수를 추가하여 사용하는 것은 Multi Thread
환경에서 위험할 수 있다. 아래의 예시를 보면 각각의 인스턴스에 count
라는 변수가 추가되어 있는데 외부에서 각 등수에 맞게 plusCount()
를 호출 할 수 있다. 하지만 멀티 쓰레드 환경에서는 각 인스턴스의 count가 공유되고 있기 때문에 조심해야 한다. public enum Rank {
THREE(3, 4_000),
FOUR(4, 10_000),
FIVE(5, 30_000);
private final int match;
private final int money;
private int count;
Rank(int match, int money) {
this.match = match;
this.money = money;
}
public void plusCount() {
this.count++;
}
}
java.lang.enum
클래스에 의해 상속된다. 자바는 다중 상속을 지원하지 않기 때문에 Enum은 다른 클래스를 상속받을 수 없다. 참고로 toString()
메서드는 상수의 이름을 리턴하도록 구현되어 있다. public static void main(String[] args) {
System.out.println(Rank.FIVE.toString());
}
// 결과 : FIVE
위에서 언급한 java.lang.Enum
클래스를 기본적으로 상속받고 있기 때문에 아래의 세 가지 메소드를 지원한다. (부모 클래스의 메소드라 사용 가능하다.)
values()
는 Enum 클래스가 가지고 있는 모든 상수 값을 배열의 형태로 리턴 한다. 참고로 단순히 String
의 형태로 단순 반환하는 것이 아니라 인스턴스를 반환하는 것이다. 즉 Enum 클래스가 가지고 있는 모든 인스턴스를 배열에 담아 반환하는 것이다. public static void main(String[] args) {
Rank[] values = Rank.values();
for(int i = 0; i< values.length; i++) {
System.out.println(values[i]);
}
}
// 실행 결과 : THREE, FOUR, FIVE
valueOf()
메서드는 String
을 파라미터로 받는데 인자로 들어온 String
과 일치하는 상수 인스턴스가 존재하면 그 인스턴스를 반환한다. 이 또한 마찬가지로 단순히 문자열을 반환하는 것이 아니라 인자로 들어온 문자열과 일치하는 인스턴스를 반환하는 것이다. public static void main(String[] args) {
System.out.println(Rank.valueOf("THREE"));
}
// 실행 결과 : THREE
public static void main(String[] args) {
Rank[] values = Rank.values();
for(int i = 0; i< values.length; i++) {
System.out.println(values[i] + "인덱스는 : " + values[i].ordinal());
}
}
// 실행 결과
THREE인덱스는 : 0
FOUR인덱스는 : 1
FIVE인덱스는 : 2
단순히 위의 예시 만으로는 Enum의 효용에 대해 느끼기 힘든 것 같습니다. 물론 클래스에 상수가 많아 진다면Enum을 활용하는 것도 방법 이겠지만 구체적으로 어떠한 경우에서 사용해야 좋은 지에 대해서는 애매하다고 생각하였습니다. 그래서 인터넷에 있는 다양한 사례들을 아래에 정리 해 보았는데요, 사례 들을 보며 어떤 식으로 사용하면 좋을 지 본인이 판단하여 사용하시면 될 것 같습니다.
public enum Winner {
WINNER("승리", Arrays.asList("kyle","pobi","hello","world")),
LOSER("패배", Arrays.asList("hodol","dunddoung","rutgo");
private final String winner;
private final List<String> list;
Winner(String winner, List<String> list) {
this.winner = winner;
this.list = list;
}
}
public boolean isWinner(String name) {
return WINNER.list.contains(name);
}
public int getWinnerSize() {
return WINNER.list.size();
}
public enum Statistic {
THREE(3, 5000),
FOUR(4, 50_000),
FIVE(5, 1_500_000),
BONUS(5, 3_000_000),
SIX(6, 2_000_000_000);
private final int matchingNumbers;
private final int prize;
Statistic(int matchingNumbers, int prize) {
this.matchingNumbers = matchingNumbers;
this.prize = prize;
}
public static Statistic getRank(int numberOfMatch) {
return Arrays.stream(values())
.filter(statistic -> statistic.matchingNumbers == numberOfMatch)
.findFirst()
.orElseThrow(new IllegalArgumentException("일치하는 번호가 3미만입니다."))
}
Enum은 자바의 익명함수 인터페이스 및 커스터마이징 익명 함수를 이용하면서 다양한 방법으로 더욱 활용 될 수 있다. 간단한 계산기 프로그램을 살펴보자.
public interface Calculator {
int calculate(int a, int b);
}
class Plus implements Calculator{
@Override
public int calculate(int a, int b) {
return a + b;
}
}
class Minus implements Calculator{
@Override
public int calculate(int a, int b) {
return a - b;
}
}
class Multiply implements Calculator{
@Override
public int calculate(int a, int b) {
return a * b;
}
}
class Divide implements Calculator{
@Override
public int calculate(int a, int b) {
if(b == 0)
throw new ArithmeticException();
return a / b;
}
}
Calculator
라는 상위 인터페이스를 두고 더하기, 빼기, 곱하기, 나누기 부분이 각각 이를 구현하는 방식을 취하고 있다. 아래는 위의 코드를 Enum 으로 옮긴 코드이다. 물론 함수형 인터페이스 자체의 강점으로 인해 코드가 간결해진 부분도 있지만 더하기와 빼기 등 연산에 대한 명확한 상수명을 함께 가지면서 내부 계산 로직 또한 같이 가지고 있다는 점이 장점이다.
import java.util.function.BiFunction;
public enum Operator {
PLUS("더하기", (a, b) -> (a + b)),
MINUS("빼기", (a, b) -> (a - b)),
MULTIPLY("곱하기", (a, b) -> (a * b)),
DIVIDE("나누기", (a, b) -> (a / b));
private final String name;
private final BiFunction<Double, Double, Double> biFunction;
Operator(String name, BiFunction<Double, Double, Double> biFunction) {
this.name = name;
this.biFunction = biFunction;
}
public Double calculate(double a, double b) {
return this.biFunction.apply(a,b);
}
}
Enum 또한 기존의 프로그래밍의 불편한 부분을 개선하기 위해 등장한 하나의 도구입니다. 기존에 클래스나 인터페이스를 통해 상수를 관리하던 것에서 Enum을 사용함으로써 IDE의 지원이나, 상태와 행위를 한 곳에서 관리한다. 등의 여러가지 장점을 가질 수 있지만 아직까지 어떠한 상황에서 Enum이 적합한지 잘 생각해 내는 것은 어려운 것 같습니다. 그래서 다음 포스팅에는 각 블로그에서 Enum을 사용했던 이야기들을 가져와서 사례를 다양하게 보면서 적절한 Enum의 사용에 대해 고민해 보는 시간을 갖도록 하겠습니다. 읽어주셔서 감사합니다.
많은 도움이 되었습니다! Enum을 잘쓰면 매우 유용할거 같습니다.