Enum 타입을 사용하면 관련 상수/기능들을 한곳에 모아두고 사용할 수 있다. 관련 데이터들을 모아두니 수정/확장하기에 편리하다. 특히 분기문 처리가 필요할 때, 가독성이 떨어지는 if-else 대신 Enum을 활용하면 코드도 훨씬 깔끔해진다.
우선 유용하게 쓸 수 있는 메소드 몇 가지를 소개해보고, 실제 if-else를 대체해서 얼마나 코드가 깔끔해지는 지 정리해보았다.
1. 상수
아래 예제에선 Enum 타입별로 관련 상수(가격과 한글메뉴명)가 할당되어있다. 여기서 Enum 내장 메소드를 활용하면 여러 기능을 처리할 수 있다.
public enum Menu {
AMERICANO(4000, "아메리카노"),
CAFE_LATTE(4500, "카페라떼"),
CAFE_MOCHA(5000, "카페모카"),
CAPPUCCINO(6000, "카푸치노"),
TEA(5500, "티");
private final int money;
private final String korean;
Menu(int money, String korean) {
this.money = money;
this.korean = korean;
}
}
우선 가장 간단하게는, 아래 getMoney()처럼 타입별 상수값을 반환할 수도 있다. 타입별 상수값을 찾을 때 사용할 수 있다.
// Enum 클래스
public int getMoney() {
return money;
}
// Test 코드
@Test
void test() {
Menu tea = Menu.TEA;
int result = tea.getMoney();
Assertions.assertThat(result).isEqualTo(5500);
}
그리고 Enum은 values() 라는 메소드를 지원하는 데, 이는 모든 Enum 타입들을 리스트로 반환해준다.
즉, Menu.values()는 모든 Menu타입을 가진 리스트이다. 이 메소드를 활용하면 상수값으로 해당 Enum 타입을 찾을 수도 있다!
System.out.println(Arrays.toString(Menu.values()));
// 출력 : [AMERICANO, CAFE_LATTE, CAFE_MOCHA, CAPPUCCINO, TEA]
상수값으로 타입 반환
아래처럼 한글 메뉴명만으로 해당 타입을 반환할 수 있다. (→ 이 방식이 if-else를 사용하지 않고 분기처리를 할 수 있게 해준다. 글 마지막에 다시 예제를 써놓았다!)
// Enum 클래스
public static Menu getMenuByKorean(String korean) {
return Arrays.stream(values()) // values() : 모든 Enum 타입 리스트 반환
.filter(i -> korean.equals(i.korean))
.findFirst()
.orElse(null);
}
// Test 코드
@Test
void test() {
Menu findMenu = Menu.getMenuByKorean("아메리카노");
Assertions.assertThat(findMenu).isEqualTo(Menu.AMERICANO);
}
타입의 존재여부
해당 타입의 존재여부도 확인할 수 있다.
// Enum 클래스
public static Boolean isMenuExist(String korean) {
return Arrays.stream(values())
.anyMatch(value -> korean.equals(value.korean));
}
// Test 코드
@Test
void test() {
Boolean isAmericanoExist = Menu.isMenuExist("아메리카노");
Boolean isMilkTeaExist = Menu.isMenuExist("밀크티");
Assertions.assertThat(isAmericanoExist).isTrue();
Assertions.assertThat(isMilkTeaExist).isFalse();
}
타입별로 같은 연산일 경우
이 예제처럼 메뉴별로 연산이 같을 경우, 갯수를 입력하면 메뉴 가격대로 곱해 최종 금액을 반환하는 메소드를 만들 수 있다.
public enum Menu {
AMERICANO(4000, "아메리카노"),
CAFE_LATTE(4500, "카페라떼"),
CAFE_MOCHA(5000, "카페모카"),
CAPPUCCINO(6000, "카푸치노"),
TEA(5500, "티");
...
public int calculateMoney (int count){ //
return money * count;
}
}
@Test
void test3() {
int result = Menu.AMERICANO.calculateMoney(2);
Assertions.assertThat(result).isEqualTo(8000);
}
근데 만약 이 예제처럼 연산식이 다 money * count 이 아니라, Enum 타입별로 다르면 어떻게 될까? ⇒ 아예 연산식을 상수화해주거나 오버라이딩해주어야 한다!
2. 연산식
Enum 타입별로 연산식이 다를 경우, 연산식 자체를 상수화해주거나, 오버라이딩해줄 수 있다. 아래는 사격 게임에서 사용자가 맞춘 과녁이 어느 구역이냐에 따라 얻는 점수가 다르다고 가정한 상황이다.
연산식 상수화
public enum Prize {
BASIC(i -> i),
DOUBLE(i -> i * 2),
BONUS(i -> i + 50),
DOUBLE_AND_BONUS(i -> i * 3);
private Function<Integer, Integer> expression;
Prize(Function<Integer, Integer> expression) {
this.expression = expression;
}
public Integer calculate(int count) {
return expression.apply(count);
}
}
오버라이딩
public enum Prize {
BASIC{
@Override
Integer calculate(int count) {
return count;
};
},
DOUBLE{
@Override
Integer calculate(int count) {
return count * 2;
};
},
BONUS{
@Override
Integer calculate(int count) {
return count + 50;
};
},
DOUBLE_AND_BONUS{
@Override
Integer calculate(int count) {
return count * 3;
};
};
abstract Integer calculate(int count);
}
두가지 방법 모두 해당 타입.calculate() 으로 사용하면 된다.
@Test
void test() {
int resultDouble = Prize.DOUBLE.calculate(100);
int resultBONUS = Prize.BONUS.calculate(100);
Assertions.assertThat(resultDouble).isEqualTo(200);
Assertions.assertThat(resultBONUS).isEqualTo(150);
}
3. Enum으로 if-else 대신하기
위에서 사격 구역 별로 점수가 다르게 반환되는 상황에서 if-else와 Enum을 비교해보았다.
if-else만 사용
if- else문만으로 구현하면 아래처럼 코드가 길고 복잡해진다. 가독성이 떨어지고, 각 분기 상황마다 뭔가를 수정해줄 때도 불편하다.
// String korean: 맞춘 구역 (“베이직”, "더블", "보너스", "더블보너스")
// int count: 몇번 맞췄는 지
void getResult(String korean, int count) {
int result;
if (korean.equals("베이직")) {
result = count;
} else if (korean.equals("더블")) {
result = count * 2;
} else if (korean.equals("보너스")) {
result = count + 50;
} else if (korean.equals("더블보너스")) {
result = count * 3;
}
return result;
}
Enum 사용
Enum을 사용해서 관련 상수/연산식을 다 모아두었다. 여기서는 연산식 자체를 상수화했다.
calculateByKorean(String korean, int count)메소드를 보면, korean값(한글 구역명)으로 Enum 타입을 찾고, 그 타입에 맞는 연산을 한다.
public enum Prize {
BASIC("베이직", i -> i),
DOUBLE("더블", i -> i * 2),
BONUS("보너스", i -> i + 50),
DOUBLE_AND_BONUS("더블보너스", i -> i * 3);
private Function<Integer, Integer> expression;
private String korean;
Prize(String korean, Function<Integer, Integer> expression) {
this.korean = korean;
this.expression = expression;
}
public Integer calculate(int count) {
return expression.apply(count);
}
public static Integer calculateByKorean(String korean, int count) {
return Arrays.stream(values())
.filter(i -> i.korean.equals(korean))
.findFirst()
.orElse(null)
.calculate(count);
}
}
이렇게 Enum을 활용하면, 아래처럼 if-else 분기문 없이 한줄로 값을 찾을 수 있다!
// String korean: 맞춘 구역 (“베이직”, "더블", "보너스", "더블보너스")
// int count: 몇번 맞췄는 지
Integer getResult(String korean, int count) {
return Prize.calculateByKorean(korean, count);
}