열거형은 쉽게 말하자면 일종의 상수 집합이다. 그리고 이러한 열거형은 다른 언어들에서도 쉽게 찾아볼 수 있다. 그리고 자바의 enum은 아래와 같은 특징과 장점이 있다.
- 자바에서 enum 은 class 처럼 활용 가능(enum type)
- type-safety한 연산 가능
- enum 에 생성자, 필드 변수, 메서드 작성 가능
- switch 문에 활용 용이
- 열거형 값들 순회 가능
- 다른 인터페이스들 implements 가능
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}
enum을 정의하는 방법은 위의 형태가 가장 기본적인 방법이다. 그리고 이를 다른 곳에서 아래와 같이 사용할 수 있다.
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.
하지만 우리가 앞서 확인했던 enum 의 특징 중에 enum 에는 필드와 생성자 그리고 메서드를 정의한다는 것이 있었다. 이를 확인하기 위해 작업별 시간과 임금을 열거형으로 통해 계산하는 예제가 있다.
class Solution {
public enum WorkSchedule{
CLEANING(10,7),
WASHING(7,8);
private final int time;
private final int wage;
WorkSchedule(int hour, int wage) {
this.time = hour;
this.wage = wage;
}
public int rest(int hours){
return this.time - hours;
}
}
public static void main(String args[]) {
WorkSchedule clean = WorkSchedule.CLEANING;
WorkSchedule wash = WorkSchedule.WASHING;
System.out.println("Cleaning Total Wage : " + clean.time * clean.wage);
System.out.println("Cleaning -2 hours : " + clean.rest(2) * clean.wage);
System.out.println("Cleaning Original Time : " + clean.time);
System.out.println("Washing Total Wage : " + wash.time * wash.wage);
System.out.println("Washing -3 hours : " + wash.rest(3) * wash.wage);
System.out.println("Washing Original Time : " + wash.time );
}
}
결과는 아래와 같이 나온다.
Cleaning Total Wage : 70
Cleaning -2 hours : 56
Cleaning Original Time : 10
Washing Total Wage : 56
Washing -3 hours : 32
Washing Original Time : 7
여기서 enum 값이 데이터를 가지고 있는 객체처럼 사용되어지는 것을 확인할 수 있다. 그리고 enum 에 정의된 메서드를 외부에서 호출해서 내부의 값은 변경되지 않으면서도 조건에 따른 값을 계산할 수 있게 만들었다. 아래는 유의 사항이다.
- enum 값이 먼저 선언되지 않으면 컴파일 에러 발생, 변수 선언처럼 마지막에 세미 콜론(;)
- 각 enum 값은 별도의 메모리에 할당, 별도의 객체로 데이터 관리
- new 키워드 사용없이 바로 enum 레퍼런스 사용
앞서 enum 이 클래스처럼 사용가능하다는 것을 확인하였다면 type-safety한 연산이 왜 가능한지도 알 수 있다.
import java.time.MonthDay;
import java.util.Iterator;
class Solution {
public enum Wage{
TEN(10);
private final int wage;
Wage(int wage) {
this.wage = wage;
}
}
public enum Time{
TEN(10);
private final int time;
Time(int time) {
this.time = time;
}
}
public static void main(String args[]) {
System.out.print("is Wage.TEN equal to Time.TEN ? ");
if(Wage.TEN.equals(Time.TEN)) System.out.println("YES");
else System.out.println("NO");
System.out.print("is Wage.TEN.wage == Time.TEN.time ? ");
if(Wage.TEN.wage == Time.TEN.time) System.out.println("YES");
else System.out.println("NO");
System.out.println("Wage TEN is : " + Wage.TEN.wage);
System.out.println("Time TEN is : " + Time.TEN.time);
System.out.println(Wage.TEN.getClass());
System.out.println(Time.TEN.getClass());
}
}
위의 결과는 아래와 같다.
is Wage.TEN equal to Time.TEN ? NO
is Wage.TEN.wage == Time.TEN.time ? YES
Wage TEN is : 10
Time TEN is : 10
class Solution$Wage
class Solution$Time
enum type이 다르면 enum 값의 리터럴이 같아도 equals() 연산을 하면 서로 다른 값이라 나온다.
자바에서 enum 은 인터페이스를 implements 하는 것은 가능하다. 반대로 내부적으로 java.lang.Enum을 상속하고 있어 다른 클래스를 extends 할 수 없다. 하지만, 이를 상속받고 있기 때문에 3가지의 메서드를 사용할 수 있다.
- values() : enum type 배열 반환
- valueOf(String name) : name 에 해당하는 enum 값 찾기, 없으면 예외 발생
- ordinal() : enum 순서 반환
이전에 다뤘던 enum의 특징 중에 순회할 수 있다는 것이 있었다. 이를 위해서는 enum 값들을 배열에 담아 반환하는 values()라는 메서드를 사용하면 된다.
class Solution {
...
public static void main(String args[]) {
WorkSchedule clean = WorkSchedule.CLEANING;
WorkSchedule wash = WorkSchedule.WASHING;
// values() 의 enum 타입 배열을 활용한 순회
for(WorkSchedule work : WorkSchedule.values()) {
System.out.println(work + " " + work.ordinal());
}
}
}
이전 예제와 같은 enum 을 사용하였고 순회하는 것을 보이기 위해 values()를 사용하는 코드를 추가하였다. 그 결과는 아래와 같다.
CLEANING 0
WASHING 1
enum에 값을 입력한 순서대로 CLEANING과 WASHING이 나온다. 그리고 ordinal()이라는 메서드를 활용해서 입력된 순서 확인도 가능하다.
아래는 우리가 찾고자 하는 값이 enum 에 존재하면 해당 값을 반환하는 valuesOf()의 예제이다.
class Solution {
...
public static void main(String args[]) {
WorkSchedule clean = WorkSchedule.CLEANING;
WorkSchedule wash = WorkSchedule.WASHING;
System.out.println(WorkSchedule.valueOf("CLEANING"));
System.out.println(WorkSchedule.valueOf("DRYING"));
}
}
주의할 점은 찾고자 하는 값이 존재하지 않으면 IllegalArgumentException 예외를 던지기 때문에 예외 처리를 잘 해주어야 한다. 아래는 그 결과이다.
CLEANING
Exception in thread "main" java.lang.IllegalArgumentException: No enum constant Solution.WorkSchedule.DRYING
at java.base/java.lang.Enum.valueOf(Enum.java:240)
at Solution$WorkSchedule.valueOf(Solution.java:5)
at Solution.main(Solution.java:27)
마지막은 EnumSet에 대한 정리이다. EnumSet은 Set인터페이스를 구현한 추상 클래스이다. 아래는 그 예제이다.
- of() : enum 값들을 EnumSet에 추가할 수 있다
- allOf(Enum Type) : 전체 값을 EnumSet에 추가할 수 있다.
import java.util.EnumSet;
import java.util.Set;
class Solution {
public enum Wage {
ONE(1),
TWO(2),
THREE(3),
FOUR(4),
FIVE(5);
private final int wage;
Wage(int wage) {
this.wage = wage;
}
}
public static void main(String args[]) {
Set<Wage> set1 = EnumSet.of(Wage.FIVE,Wage.THREE);
EnumSet<Wage> set2 = EnumSet.allOf(Wage.class);
System.out.println(set1);
System.out.println(set2);
}
}
결과는 아래와 같이 나온다.
[THREE, FIVE]
[ONE, TWO, THREE, FOUR, FIVE]
EnumSet은 이렇게 enum 값들의 집합을 만들어서 필요한 연산을 할 때 유용하게 사용될 수 있다.