[Java Study] - enum type
Enum Type (열거형)
열거형이란 ?
관련된 상수들을 같이 묶어 놓은 special class 이다.
JDK 1.5 이후 부터 열거형을 사용 할 수 있었는데, java 에서는 변수 , 메서드, 생성자를 열거형에 추가할 수 있기 때문에
C 나 C++ 보다 더욱 향상된 성능으로 Enum 클래스를 사용할 수 있다.
열거형 정의
enum 열거형이름 {
상수명1, 상수명2
}
class 밖에서 선언
enum Color {
RED, GREEN, BLUE;
}
public class Test {
public static void main(String[] args) {
Color c1 = Color.RED;
System.out.println(c1);
}
}
class 안에서 선언
public class Test {
enum Color {
RED, GREEN, BLUE;
}
public static void main(String[] args) {
Color c1 = Color.RED;
System.out.println(c1);
}
}
열거형 이점
JDK 1.5 이전에 Enum Type 이 탄생하기 전에는
public class EnumTest {
private static final int RED = 1;
private static final int GREEN = 2;
private static final int BLUE = 3;
public static void main(String[] args) {
System.out.println(RED + GREEN / BLUE); // 의미가 없는 연산일 뿐
}
}
상수가 필요할 때마다 임의의 정수를 지정해줬다.
열거된 상수는 정수이기 때문에 산술 계산이 가능한데
계산을 하려고 정의한 것이 아니기 때문에 이것은 의미가 없다
또한 ,
기존의 color 를 의미하는 red 외에 추가적으로 rainbow 라는 정보를 담기 위해 상수를 지정해줬는데 ,
이때 이름의 충돌이 발생하기 때문에 이를 방지하기 위해 고유 식별자 형식을 앞에 붙어주었다. ( COLOR_ , RAINBOW_)
public class EnumTest {
private static final int COLOR_RED = 1;
private static final int COLOR_GREEN = 2;
private static final int COLOR_BLUE = 3;
private static final int RAINBOW_RED = 1;
private static final int RAINBOW_GREEN = 2;
private static final int RAINBOW_BLUE = 3;
public static void main(String[] args) {
System.out.println(COLOR_RED == RAINBOW_RED); // true
}
}
rainbow 라는 정보를 추가하였지만 여전히 문제가 남아있다.
COLOR_RED == RAINBOW_RED 는 사실 의미상 false 이어야 하지만
true가 출력된다.
정수로 지정했기 때문에 컴파일러 입장에서는 정수 값을 비교하는 셈이니 컴파일 에러가 발생하지 않는다.
즉 서로 다른 개념인데 비교가 가능하다는 문제가 발생한다.
Enum Type 으로 해결
Color 와 RainBow 서로 다른 개념이기 때문에 비교가 불가능하고
namespace 충돌이 발생하지 않는다.
또 다른 이점으로 switch문 인수로 전달이 가능하다는 것이다.
사용자 정의 타입은 switch 문을 사용할 수 없다.
컴파일 에러 발생 :
Incompatible types. Found: 'step11.Color', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum
반면 enum 타입은 사용이 가능하다.
enum Color {
RED, GREEN, BLUE;
}
public class Test {
public static void main(String[] args) {
Test test = new Test();
test.print(Color.BLUE);
}
public void print(Color color) {
switch (color) {
case RED:
System.out.println("빨간색 입니다.");
break;
case BLUE:
System.out.println("파란색 입니다.");
break;
case GREEN:
System.out.println("초록색 입니다. ");
break;
}
}
}
실행 결과
파란색 입니다.
java.lang.Enum
public enum Day {
SUNDAY , MONDAY , TUESDAY , WEDNESDAY , THURSDAY , FRIDAY , SATURDAY
}
위에 있는 Day 클래스의 바이트 코드를 살펴보자
javap -c Day.class
모든 field 앞에 static final 키워드가 붙어있기 때문에
여태껏 , enum 의 이름에 접근 할 수 있었다. ex) Day.SUNDAY
// Day는 java.lang.Enum 클래스를 상속 받는 final 클래스이다.
public final class step11.Day extends java.lang.Enum<step11.Day> {
// 모든 field 는 static final로 상수이다.
public static final step11.Day SUNDAY;
public static final step11.Day MONDAY;
public static final step11.Day TUESDAY;
public static final step11.Day WEDNESDAY;
public static final step11.Day THURSDAY;
public static final step11.Day FRIDAY;
public static final step11.Day SATURDAY;
// values() 메서드를 구현
public static step11.Day[] values();
Code:
0: getstatic #1 // Field $VALUES:[Lstep11/Day;
3: invokevirtual #2 // Method "[Lstep11/Day;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[Lstep11/Day;"
9: areturn
// valueOf() 메서드 구현
public static step11.Day valueOf(java.lang.String);
Code:
0: ldc #4 // class step11/Day
2: aload_0
3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #4 // class step11/Day
9: areturn
static {}; // 모든 멤버를 static { } 으로 초기화
Code:
0: new #4 // class step11/Day
3: dup
4: ldc #7 // String SUNDAY
6: iconst_0
7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #9 // Field SUNDAY:Lstep11/Day;
13: new #4 // class step11/Day
16: dup
17: ldc #10 // String MONDAY
19: iconst_1
20: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
23: putstatic #11 // Field MONDAY:Lstep11/Day;
26: new #4 // class step11/Day
29: dup
30: ldc #12 // String TUESDAY
32: iconst_2
33: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
36: putstatic #13 // Field TUESDAY:Lstep11/Day;
39: new #4 // class step11/Day
42: dup
43: ldc #14 // String WEDNESDAY
45: iconst_3
46: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
49: putstatic #15 // Field WEDNESDAY:Lstep11/Day;
52: new #4 // class step11/Day
55: dup
56: ldc #16 // String THURSDAY
58: iconst_4
59: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
62: putstatic #17 // Field THURSDAY:Lstep11/Day;
65: new #4 // class step11/Day
68: dup
69: ldc #18 // String FRIDAY
71: iconst_5
72: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
75: putstatic #19 // Field FRIDAY:Lstep11/Day;
78: new #4 // class step11/Day
81: dup
82: ldc #20 // String SATURDAY
84: bipush 6
86: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
89: putstatic #21 // Field SATURDAY:Lstep11/Day;
92: bipush 7
94: anewarray #4 // class step11/Day
97: dup
98: iconst_0
99: getstatic #9 // Field SUNDAY:Lstep11/Day;
102: aastore
103: dup
104: iconst_1
105: getstatic #11 // Field MONDAY:Lstep11/Day;
108: aastore
109: dup
110: iconst_2
111: getstatic #13 // Field TUESDAY:Lstep11/Day;
114: aastore
115: dup
116: iconst_3
117: getstatic #15 // Field WEDNESDAY:Lstep11/Day;
120: aastore
121: dup
122: iconst_4
123: getstatic #17 // Field THURSDAY:Lstep11/Day;
126: aastore
127: dup
128: iconst_5
129: getstatic #19 // Field FRIDAY:Lstep11/Day;
132: aastore
133: dup
134: bipush 6
136: getstatic #21 // Field SATURDAY:Lstep11/Day;
139: aastore
140: putstatic #1 // Field $VALUES:[Lstep11/Day;
143: return
}
위의 Day enum 클래스 처럼 모든 열거형은 Enum 의 자손이다.
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
/**
* The name of this enum constant, as declared in the enum declaration.
* Most programmers should use the {@link #toString} method rather than
* accessing this field.
*/
private final String name;
/**
* Returns the name of this enum constant, exactly as declared in its
* enum declaration.
*
* <b>Most programmers should use the {@link #toString} method in
* preference to this one, as the toString method may return
* a more user-friendly name.</b> This method is designed primarily for
* use in specialized situations where correctness depends on getting the
* exact name, which will not vary from release to release.
*
* @return the name of this enum constant
*/
public final String name() {
return name;
}
....
...
...
}
보다시피 , Enum 은 추상 클래스이기 때문에, 객체를 생성할 수 없다.
그렇기 때문에 생성자를 직접적으로 호출할 수 없다.
(enum에서 public 또는 protected 생성자를 만들게 되면 컴파일 에러가 발생한다.)
javap -p Day.class
Compiled from "Day.java"
public final class step11.Day extends java.lang.Enum<step11.Day> {
public static final step11.Day MONDAY;
public static final step11.Day FRIDAY;
public static final step11.Day WEDNESDAY;
public static final step11.Day THURSDAY;
public static final step11.Day TUESDAY;
public static final step11.Day SATURDAY;
public static final step11.Day SUNDAY;
private static final step11.Day[] $VALUES;
public static step11.Day[] values();
public static step11.Day valueOf(java.lang.String);
private step11.Day();
static {};
}
enum 은 기본적으로 private 생성자를 포함하고 있다.
또한, 내부적으로 java.lang.Enum 클래스를 상속 받고 있기 때문에
단일 상속 원칙으로 다른 클래스를 상속 받을 수 없다.
사용자 지정 값 할당하기
각 enum 에 위와 같이 값을 지정할 수 있다.
하지만 컴파일 에러가 발생하는데 ,
매개변수가 있는 생성자가 없기 때문이다.
public enum Day {
MONDAY("월요병"), TUESDAY("화요팅"),
WEDNESDAY("벌써 수요일"), THURSDAY("목요팅"),
FRIDAY("불금"), SATURDAY("알고리즘 공부하는 날"), SUNDAY("내일 출근 ㅠ");
private String comment;
Day(String comment) {
this.comment = comment;
}
}
값을 담을 필드와 초기화시 사용할 생성자를 만들었다.
public enum Day {
MONDAY("월요병"), TUESDAY("화요팅"),
WEDNESDAY("벌써 수요일"), THURSDAY("목요팅"),
FRIDAY("불금"), SATURDAY("알고리즘 공부하는 날"), SUNDAY("내일 출근 ㅠ");
private String comment;
Day(String comment) {
this.comment = comment;
}
public String getComment() {
return comment;
}
}
그리고 값을 꺼내기 위해 메서드 또한 생성하였다.
public class Main {
public static void main(String[] args) {
Day sunday = Day.SUNDAY;
System.out.println(sunday.getComment());
}
}
실행 결과
내일 출근 ㅠ
메서드
메서드 | 설명 |
T values() | 해당 열거형의 모든 상수를 저장한 배열을 생성하여 반환한다. 위의 바이트코드에 보면 나와있듯이 , 자바의 모든 열거형에 컴파일러가 자동으로 추가해 주는 메소드이다. |
T valueOf(String value) | 전달된 문자열과 일치하는 해당 열거형의 상수를 반환한다. 이 메소드 또한 컴파일러가 자동으로 추가해준다. |
int ordinal() | 해당 열거형 상수가 정의된 순서를 반환한다. 이때 반환 값은 열거형 정의에서 해당 상수가 정의된 순서이지, 상수 값이 아님을 명심해야 한다. |
String name() | 열거형 상수의 이름을 문자열로 반환 |
public enum Day {
SUNDAY , MONDAY , TUESDAY , WEDNESDAY , THURSDAY , FRIDAY , SATURDAY
}
values()
public class Test {
public static void main(String[] args) {
Day[] values = Day.values();
for (Day day : values) {
System.out.println(day);
}
}
}
실행 결과
SUNDAY
MONDAY
TUESDAY
WEDNESDAY
THURSDAY
FRIDAY
SATURDAY
valueOf()
public class Test {
public static void main(String[] args) {
Day day = Day.valueOf("SUNDAY");
System.out.println(day);
}
}
실행 결과
SUNDAY
해당 열거형 상수 중에 일치하는 값이 없으면 java.lang.IllegalArgumentException 이 발생한다.
public static <T extends Enum<T>> T valueOf(Class<T> enumType,
String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}
SUNDAY , MONDAY , TUESDAY , WEDNESDAY , THURSDAY , FRIDAY , SATURDAY 중에 일치하는 상수가 없다.
public class Test {
public static void main(String[] args) {
Day day = Day.valueOf("HOLIDAY");
System.out.println(day); // java.lang.IllegalArgumentException
}
}
ordinal()
public enum Day {
SUNDAY , MONDAY , TUESDAY , WEDNESDAY , THURSDAY , FRIDAY , SATURDAY
// 0 1 2 3 4 5 6
}
public class Test {
public static void main(String[] args) {
int ordinal = Day.TUESDAY.ordinal();
System.out.println(ordinal);
}
}
실행 결과
2
여기서 상수를 정의하는 순서를 변경해보자
public enum Day {
MONDAY ,FRIDAY , WEDNESDAY , THURSDAY , TUESDAY , SATURDAY , SUNDAY
// 0 1 2 3 4 5 6
}
public class Test {
public static void main(String[] args) {
int ordinal1 = Day.TUESDAY.ordinal();
int ordinal2 = Day.SUNDAY.ordinal();
System.out.println(ordinal1);
System.out.println(ordinal2);
}
}
실행 결과
4
6
name()
public class Test {
public static void main(String[] args) {
System.out.println(Day.MONDAY.name());
System.out.println(Day.THURSDAY.name());
System.out.println(Day.WEDNESDAY.name());
System.out.println(Day.SUNDAY.name());
}
}
실행 결과
MONDAY
THURSDAY
WEDNESDAY
SUNDAY
열거형 비교
열거형 상수에 비교 연산자는 사용 할 수 없다. 컴파일 에러 발생
대신 compareTo() 메서드로 비교 할 수 있다.
enum Color {
// 0 1 2 3 4 5
RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE
}
public class Test {
public static void main(String[] args) {
System.out.println(Color.RED.compareTo(Color.YELLOW)); // -2
System.out.println(Color.RED.compareTo(Color.PURPLE)); // -5
System.out.println(Color.BLUE.compareTo(Color.GREEN)); // 1
}
}
실행 결과
-2
-5
1
== 연산자와 equals() 메서드
두 개의 enum 이 일치하는지 확인 할 때 사용한다.
enum Color {
RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE
}
public class Test {
Color color;
public Test(Color color) {
this.color = color;
}
public static void main(String[] args) {
Test test = new Test(Color.ORANGE);
System.out.println(test.color == Color.ORANGE);
System.out.println(test.color.equals(Color.ORANGE));
System.out.println(test.color == Color.RED);
System.out.println(test.color.equals(Color.RED));
}
}
실행 결과
true
true
false
false
그렇다면 == 연산자와 equals() 차이점은 무엇일까?
enum Color {
RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE
}
public class Test {
Color color;
public Test() {
}
public static void main(String[] args) {
Test test = new Test();
System.out.println(test.color == Color.BLUE); // false
}
}
실행 결과
false
== 연산자는 NullPointerException이 발생하지 않는다.
enum Color {
RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE
}
public class Test {
Color color;
public Test() {
}
public static void main(String[] args) {
Test test = new Test();
System.out.println(test.color.equals(Color.BLUE)); // java.lang.NullPointerException 발생
}
}
반면에 equals() 메서드는 NullPointerException이 발생한다.
서로 다른 enum 타입일 때 사용해보면
== 연산자에서는 컴파일 에러가 발생한다.
반면 eqauls() 메서드는 컴파일 에러가 발생하지 않는다.
enum Color {
RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE
}
enum Rainbow {
RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}
public class Test {
public static void main(String[] args) {
System.out.println(Color.RED.equals(Rainbow.RED)); // false
}
}
단지 false 만 반환할 뿐이다.
http://tcpschool.com/java/java_api_enum
https://www.geeksforgeeks.org/enum-in-java/
https://titanwolf.org/Network/Articles/Article?AID=bec9bb38-9f61-4f30-be4b-3b63e6b3ef7b
https://www.geeksforgeeks.org/comparing-enum-members-java/?ref=rp
자바의 정석