enum 이란?
enum 타입은 변수가 미리 정의된 상수 집합이 될 수 있게 하는 특수한 데이터 타입이다. 변수는 변수에 대해 미리 정의 된 값 중 하나와 같아야 한다. 흔한 예로 나침반 방향(NORTH, SOUTH, EAST, WEST)이나 요일이 있다.
상수이기 때문에 enum 타입의 필드 이름은 대문자로 표현한다.
Java 에서는 enum 키워드를 사용하여 enum 타입을 정의한다.
enum의 장점
- 코드가 단순해지며 가독성이 좋아진다.
- IDE를 이용할 때 컴파일러가 자동으로 오타를 찾아주는 등 편리하다.
- 변경에 유연하다.
- 데이터들의 관계를 표현하기 좋다.
enum 사용법
여러 사례들을 통해 enum 사용법에 대해 알아보자.
1. 단순 enum 패턴
다음은 요일에 대한 enum type을 정의한다.
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}
요일에 따라 switch-case 문을 동작시킴으로써 코드의 직관성을 높여준다.
public class EnumTest {
Day day;
public EnumTest(Day day) {
this.day = day;
}
public void tellItLikeItIs() {
switch (day) {
case MONDAY:
System.out.println("Mondays are bad.");
break;
case FRIDAY:
System.out.println("Fridays are better.");
break;
case SATURDAY: case SUNDAY:
System.out.println("Weekends are best.");
break;
default:
System.out.println("Midweek days are so-so.");
break;
}
}
public static void main(String[] args) {
EnumTest firstDay = new EnumTest(Day.MONDAY);
firstDay.tellItLikeItIs();
EnumTest thirdDay = new EnumTest(Day.WEDNESDAY);
thirdDay.tellItLikeItIs();
EnumTest fifthDay = new EnumTest(Day.FRIDAY);
fifthDay.tellItLikeItIs();
EnumTest sixthDay = new EnumTest(Day.SATURDAY);
sixthDay.tellItLikeItIs();
EnumTest seventhDay = new EnumTest(Day.SUNDAY);
seventhDay.tellItLikeItIs();
}
}
결과는 아래와 같다.
Mondays are bad.
Midweek days are so-so.
Fridays are better.
Weekends are best.
Weekends are best.
2. 다른 행성에서 몸무게 구하기
행성에 따라서 나의 몸무게를 구하는 프로그램을 만들어보자.
만약 지구에서 몸무게가 56kg 나가는 사람이 있다고 하자. 각 행성별 중력은 다음의 표와 같이 비례한다고 가정하자.
if문의 조건에 따라 행성에 따른 몸무게를 표현할 수 있을 것이다.
private void printWeight(String where) {
int myWeight = 56;
int result;
if (where.equals("지구")) {
result = getWeightOnEarth(myWeight);
System.out.println("지구에서 나의 몸무게는 : " + result);
}
if (where.equals("토성")) {
result = getWeightOnSaturn(myWeight);
System.out.println("토성에서 나의 몸무게는 : " + result);
}
if (where.equals("목성")) {
result = getWeightOnJupiter(myWeight);
System.out.println("목성에서 나의 몸무게는 : " + result);
}
}
이러한 방식은 당장은 직관적이고 괜찮아보이지만, 행성의 종류가 늘어났을 때 늘어나는 if문과 각 행성별로 구현해야하는 getWeightOn~메서드를 감당할 수 없을것이다. enum을 사용해서 이를 해결해보자.
public enum Planet {
EARTH(value -> value * 1, "지구"),
SATURN(value -> value * 2, "토성"),
JUPITER(value -> value * 3, "목성");
Planet(Function<Integer, Integer> expression, String where) {
this.expression = expression;
this.where = where;
}
private Function<Integer, Integer> expression;
private String where;
public int getWeight(int value) {
return expression.apply(value);
}
public String getWhere() {
return where;
}
}
자바에서의 enum은 필드와 함수를 포함할 수 있다. 각 행성들이 함수형 인터페이스 Function을 구현하도록 함으로써 행성별로 몸무게를 구하는 함수를 한눈에 확인할 수 있다. 이렇게 하면 새로운 행성이 추가되더라도 수정할 내용이 매우 적어진다.
참고 : enum 타입 생성자는 항상 private 이어야 하며 직접 호출할 수 없다.
다음으로 enum을 사용하는 코드를 확인해보자.
private void printWeight(String where) {
int myWeight = 56;
for (Planet p : Planet.values()) {
if (p.getWhere().equals(where)) {
System.out.println(where + "에서 나의 몸무게는 : " + p.getWeight(myWeight));
}
}
}
enum 클래스는 내부적으로 java.lang.Enum을 상속하도록 구현되어있다. java.lang.Enum 에는 values()가 구현되어있는데 enum 의 모든 원소를 배열로 리턴한다. (여기서 다른언어의 enum 보다 java의 enum이 강력한 기능을 보유한다는 것을 알 수 있다.)
3. 데이터 그룹 관리하기
배달어플중에서 음식 카테고리를 구현한다고 해보자.
음식들의 카테고리와 카테고리에 속하는 음식들은 다음과 같다.
카테고리 |
음식 |
한식 |
김치찌개, 된장찌개, 삼겹살 |
중식 |
짜장면, 짬뽕 |
패스트푸드 |
치킨, 햄버거, 피자 |
이 관계를 enum 으로 다음과 같이 구현할 수 있다.
public enum KindOfFood {
KOREAN_FOOD("한식", Arrays.asList("김치찌개", "된장찌개", "삼겹살")),
CHINESE_FOOD("중식", Arrays.asList("짜장면", "짬뽕")),
FAST_FOOD("패스트푸드", Arrays.asList("치킨", "햄버거", "피자"));
private String kind;
private List<String> foods;
KindOfFood(String kind, List<String> foods) {
this.kind = kind;
this.foods = foods;
}
}
이러한 경우, 파라미터로 전달받은 음식의 이름이 잘못되었을 경우 문제가 생기는 등 관리가 어렵다.
public enum Food {
KIMCH_STEW("김치찌개"),
SOYBEAN_PASTE_STEW("된장찌개"),
PORK_BELLY("삼겹살"),
JAJANGMYEON("짜장면"),
CHAMPON("짬뽕"),
CHICKEN("치킨"),
PIZZA("피자"),
HAMBURGER("햄버거");
private String name;
Food(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
이렇게 Food enum을 정의해 음식 데이터 그룹을 관리할 수 있다. 이제 문자열을 enum으로 바꿔주자.
public enum KindOfFood {
KOREAN_FOOD("한식", Arrays.asList(Food.KIMCH_STEW, Food.SOYBEAN_PASTE_STEW, Food.PORK_BELLY)),
CHINESE_FOOD("중식", Arrays.asList(Food.JAJANGMYEON, Food.CHAMPON)),
FAST_FOOD("패스트푸드", Arrays.asList(Food.CHICKEN, Food.HAMBURGER, Food.PIZZA));
private String kind;
private List<Food> foods;
KindOfFood(String kind, List<Food> foods) {
this.kind = kind;
this.foods = foods;
}
}
여태까지는 데이터 그룹을 표현하기 위한 코드만 작성했다.
KindOfFood 를 실제로 사용하기 위해 메서드를 약간 추가해보자.
public static KindOfFood findKindByFood(Food food) {
return Arrays.stream(KindOfFood.values())
.filter(KindOfFood -> KindOfFood.hasFood(food))
.findAny()
.get();
}
private boolean hasFood(Food food) {
return foods.stream()
.anyMatch(name -> name == food);
}
public String getKind() {
return kind;
}
findKindByFood는 Food를 파라미터로 전달받아 해당 Food가 어느 카테고리(KindOfFood) 에 속하는지를 찾아주는 메서드이다. 이를 사용해보자.
public static void main(String[] args) {
Food food = Food.PIZZA;
KindOfFood kindOfFood = KindOfFood.findKindByFood(food);
System.out.println(kindOfFood.getKind());
}
결과는 :
패스트푸드