앞선 글에서 Enum 타입에 등장에 관한 글을 썻다. Enum 타입이 등장하게 된 배경은 개인적으로 정리하기를 아래와 같다.
Enum 타입의 등장
1. 상수 클래스는 의미의 모호성과 타입의 불안정이 존재.
- 서로 다른 의미를 지닌 상수의 존재가 절대적이지 못하다. 기본형 타입으로 저장되기 때문에 이후에 값의 변경이 일어난다 하더라도 컴파일 시점에 발견하기 어렵다. 따라서 런타임 에러는 안나지만 해당 소스코드는 문제점을 잠재적으로 가지고 있다.
2. 인터페이스의 필드는 static final 의 선언이지만 여전히 상수가 불안정
- 내부를 살펴보면 여전히 기본형 혹은 참조형으로 타입을 가지기 때문에 컴파일보단 런타임에 문제가 발생하는 경우가 있을 수 있다.
[ 이 게시글을 작성한 이후 enum 을 사용한 후기 ]
Enum 은 본인이 어떻게 사용하느냐에 따라서 활용도가 무궁무진하다고 생각한다. 나도 이번에 enum 타입을 공부하면서 코드의 리팩토링이 간결해지고 유지보수에 대해 소요하는 시간과 이해가 대폭 감소됨을 느꼈다.
비록 내가 해본 프로젝트의 규모가 커봤자 얼마나 커지고, 복잡도가 높아봐야 어느정도 수준이겠냐만.. 내가 느낀 감정 그대로 적어보고 싶다.
- 타입이 안정적이기 때문에 리팩토링 및 유지보수가 한결 수월하다.
- 네임스페이스를 제공하기 때문에 가독성이 우수하다.
- 열거형 타입 그 자체이기 때문에 switch 문에서 효과적이다. (비교측면)
- 열거형 타입은 하나의 속성에 대한 여러 내용들을 구분짓기에 매우 좋다.
- 하드코딩된 내용들을 열거형 타입으로 작성하였기 때문에 관리가 쉽다.
나도 Enum에 대한 글을 많이 보았고 예시도 따라치고 여러 글들을 보면서 나름의 코드를 구현하면서 공부했다. 하지만 여전히 부족하고 단순히 나의 생각이기 때문에 명확하지 않다. 혹시 이 글을 읽고 있다면 보는 것에 그치지 말고 본인이 직접 해보길...
Enum 예시
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | package Enum; import java.io.Closeable; import java.io.IOException; public enum ThreadStatesEnum implements Closeable{ START(1){ @Override public String toString(){ return "START implements. Priority = " + getPriority(); } @Override public String getDetail(){ return "START"; } }, RUNNING(2){ @Override public String getDetail(){ return "RUNNING"; } }, WAITING(3){ @Override public String getDetail(){ return "WAITING"; } }, DEAD(4){ @Override public String getDetail(){ return "DEAD"; } }; // Enum 타입 내부의 기본형 필드 private int priority; // Enum 타입 내부의 추상메소드 public abstract String getDetail(); // Enum 타입의 생성자는 항상 접근지정자가 private private ThreadStatesEnum(int i){ priority = i; } // Enum 은 메소드를 가질 수 있다. public int getPriority(){ return this.priority; } public void setPriority(int p){ this.priority = p; } // Enum 은 메소드 오버라이딩이 가능하다. @Override public String toString(){ return "Default ThreadStatesConstructors implementation. Priority = " + getPriority(); } @Override public void close() throws IOException{ System.out.println("Close of Enum"); } } | cs |
enum 으로 할 수 있는 모든 것들을 나타낸다. enum 또한 클래스의 일종으로 필드들은 public static final 의 형태로 외부에서 접근가능하지만 생성자가 private 으로 선언되었기 때문에 인스턴스는 만들지 못하도록 금지하였다.
코드를 보면 네가지 상수가 존재한다.
(1) START (1) { - }
(2) RUNNING (2) { - }
(3) WAITING (3) { - }
(4) DEAD (4) { - }
enum 필드는 private 으로 선언되어있으며 내부에 추상메소드가 있다. 추상메소드는 해당 상수 내부에서 오버라이딩해줘야 한다. 하지만 열거형 내부에 메소드 오버라이딩을 할 경우가 있으면 나의 생각이지만 일반적인 메소드를 이용하는 것이 효율적이라고 생각한다.
왜냐하면 추상메소드의 enum 내부 상수들 전체에 오버라이딩 해주기 때문에 의도치 않게 코드를 만들 수 있기 때문이여 코드 자체가 지저분해진다. 따라서 추상메소드 abstract 키워드를 쓰는 것보단 일반 메소드로 정의해놓고 해당 열거형에만 오버라이딩 해주면 열거형 내부에서 따로 컨트롤할 수 있다.
생성자는 private 으로 인스턴스를 생성하지 않지만 내부 열거형들은 하나의 파라미터 값을 순서대로 보내고 있는데, 저것은 결과적으로 해당 enum 필드의 값을 초기화해주고 있다. 외부에서 바로 해당 필드로 접근하지 못하게 하고 해당 필드는 getter() 를 통해서 값을 얻도록 해주고 있는 것이다.
Enum Type 정리
enum 은 인터페이스를 구현할 수 있다. (인터페이스 상속을 허용)
enum 의 생성자는 항상 private 이다. (생성자는 디폴트 혹은 매개변수 있는 생성자)
new 연산자를 통해 enum 을 인스턴스화 하지 못한다.
enum 은 추상메소드를 선언할 수 있으며, 모든 enum 상수는 추상메소드를 반드시 구현하여야 한다.
enum 상수는 네임스페이스가 가능하다. 위의 예시에서 ThreadStatesEnum.START 가 가능하는 의미.
enum 필드를 새롭게 추가 가능하다.
enum 상수는 대문자와 언더바(_) 로 작성한다.
enum 상수는 기본이 static final 이다.
enum 필드는 언제든지 변경이 가능하다.
enum 상수는 static final 이기 때문에 "==" 혹은 equals() 메소드를 사용하여 구별이 가능하다.
이제는 위의 예시를 사용해보는 코드를 확인하자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | package Enum; import java.io.IOException; import java.util.EnumMap; import java.util.EnumSet; import java.util.Map.Entry; import java.util.Set; public class JavaEnumExamples { public static void main(String[]args) throws IOException{ // usingEnumMethods(); // // usingEnumValueOf(); // // usingEnumValues(); // // usingEnumInSwitch(ThreadStatesEnum.START); // usingEnumInSwitch(ThreadStatesEnum.DEAD); // // usingEnumSet(); // // usingEnumMap(); } private static void usingEnumMap(){ /** * * EnumMap 에서는 null 을 Key 로써 사용하지 못하며, 동기화되지 않는다. * * **/ EnumMap<ThreadStatesEnum, String> enumMap = new EnumMap<ThreadStatesEnum, String>(ThreadStatesEnum.class); enumMap.put(ThreadStatesEnum.START, "Thread is started"); enumMap.put(ThreadStatesEnum.RUNNING, "Thread is running"); enumMap.put(ThreadStatesEnum.WAITING, "Thread is waiting"); enumMap.put(ThreadStatesEnum.DEAD, "Thread is dead"); Set<ThreadStatesEnum> keySet = enumMap.keySet(); for(ThreadStatesEnum key : keySet){ System.out.println("key = " + key.toString()); } // Key, Value 쌍으로 들어오는 값들 for(Entry<ThreadStatesEnum, String> entrySet : enumMap.entrySet()){ System.out.println(entrySet.getKey()); System.out.println(entrySet.getValue()); System.out.println("=================="); } // >> key = START implements. Priority = 1 // >> key = Default ThreadStatesConstructors implementation. Priority = 2 // >> key = Default ThreadStatesConstructors implementation. Priority = 3 // >> key = Default ThreadStatesConstructors implementation. Priority = 4 // >> START implements. Priority = 1 // >> Thread is started // >> ================== // // >> Default ThreadStatesConstructors implementation. Priority = 2 // >> Thread is running // >> ================== // // >> Default ThreadStatesConstructors implementation. Priority = 3 // >> Thread is waiting // >> ================== // // >> Default ThreadStatesConstructors implementation. Priority = 4 // >> Thread is dead // >> ================== } private static void usingEnumSet(){ /** * * EnumSet 은 동기화되지 않으며 null 요소는 허용되지 않는다. * 또한 Copy(Collection<E> c) or complementOf(EnumSet<E> s) * 와 같은 유용한 메소드를 제공한다. * * **/ EnumSet<ThreadStatesEnum> enumSet = EnumSet.allOf(ThreadStatesEnum.class); for(ThreadStatesEnum testEnum : enumSet){ System.out.println("Using EnumSet, Priority = " + testEnum.getPriority()); } // >> Using EnumSet, Priority = 1 // >> Using EnumSet, Priority = 2 // >> Using EnumSet, Priority = 3 // >> Using EnumSet, Priority = 4 } private static void usingEnumInSwitch(ThreadStatesEnum th) { /** * * 스위치 케이스에서 enum 상수를 사용하기. * * **/ switch (th) { case START: System.out.println("START thread"); break; case WAITING: System.out.println("WAITING thread"); break; case RUNNING: System.out.println("RUNNING thread"); break; case DEAD: System.out.println("DEAD thread"); } // -- 두 개의 출력이 나타난다. // -- usingEnumInSwitch(ThreadStatesEnum.START); // -- usingEnumInSwitch(ThreadStatesEnum.DEAD); // >> START thread // >> DEAD thread } private static void usingEnumValues(){ /** * * (1) values() 메소드는 enum 에 포함된 모든 값을 가져온다. * (2) 해당 메소드는 자바 컴파일러에 의해 자동으로 생성된다. * +) java.util.Enum 클래스에는 values() 구현이 없다. * * **/ ThreadStatesEnum[] thArray = ThreadStatesEnum.values(); for(ThreadStatesEnum th : thArray){ System.out.println(th.name()); // enum 상수 필드 System.out.println(th.toString() + " :: Priority = " + th.getPriority()); System.out.println(); /** [ for(ThreadStatesEnum th : thArray) 반복 구문 내부 출력] **/ // >> START // >> START implements. Priority = 1 :: Priority = 1 // // >> RUNNING // >> Default ThreadStatesConstructors implementation. Priority = 2 :: Priority = 2 // // >> WAITING // >> Default ThreadStatesConstructors implementation. Priority = 3 :: Priority = 3 // // >> DEAD // >> Default ThreadStatesConstructors implementation. Priority = 4 :: Priority = 4 } } private static void usingEnumValueOf(){ /** * * (1) Enum.valueOf() 메소드를 이용하면, String 으로부터 enum 객체를 생성이 가능하다. * (2) Enum.valueOf(enumType, String) 으로 해당 String 열거형이 없는 경우 런타임 에러 발생 * * **/ ThreadStatesEnum th = Enum.valueOf(ThreadStatesEnum.class, "START"); System.out.println("th Priority = " + th.getPriority()); // >> th Priority = 1 } private static void usingEnumMethods() throws IOException{ /** * * (1) 어떻게 enum 객체를 생성하는지 * (2) enum 객체에 대한 메소드를 이용하는 방법 * (3) setPriority 를 활용해 enum 변수를 변경하는 방법 * * **/ ThreadStatesEnum thc = null; thc = ThreadStatesEnum.DEAD; System.out.println("Priority is : " + thc.getPriority()); // >> Priority is : 4 thc = ThreadStatesEnum.DEAD; System.out.println("Using overriden method. : " + thc.toString()); // >> Using overriden method. : Default ThreadStatesConstructors implementation. Priority = 4 thc = ThreadStatesEnum.START; System.out.println("Using overriden method. : " + thc.toString()); // >> Using overriden method. : START implements. Priority = 1 thc.setPriority(10); System.out.println("Enum Constant variable changed priority value : "+thc.getPriority()); // >> Enum Constant variable changed priority value : 10 thc.close(); // >> Close of Enum } } | cs |
'jvm lang' 카테고리의 다른 글
20180331 JVM Memory Model ( Garbage Collector : GC ) (0) | 2018.03.31 |
---|---|
20180328 NewsCrawler Implements in Java (0) | 2018.03.28 |
20180221 Enum Type (Enumeration) 01 (0) | 2018.02.21 |
20180219 instanceof 연산자 (0) | 2018.02.19 |
20180214 Use UNIVocity-Parser (tsv, csv, json) (0) | 2018.02.14 |