해당 글은 아래의 자료들을 바탕으로 작성하였습니다.자세한 내용은 아래의 글을 확인해주세요.내용, 저작권등의 문제가 있는 경우 댓글이나 메일 부탁드립니다.
- 우아한형제들 기술 블로그 Java Enum 활용기
- Java enum (열거형의 다양한 사용법)
- 번역 자바 Enum의 10가지 예제
- Java enum의 뿌리를 찾아서...
- 책 : 이팩티브 자바3 - 2장 (싱글톤을 구현하기 위한 가장 좋은 방법)
- 책 : 이펙티브 자바3 - 6장 열거형타입 및 어노테이션
- 책 : 자바 ORM 표준 JPA 프로그래밍
자바의 열거 타입은 완전한 형태의 클래스라서 (단순한 정숫값일 뿐인) 다른 언어의 열거 타입보다 훨씬 강력하다. - effective java 3
enum을 이용하면 코드의 중복, if문의 사용등을 줄여 코드를 간결하고 보기 좋게 만들 수 있다.
기본적인 열거형으로의 사용도 기존의 정수열거패턴 보다 장점이 많다.
public static final int APPLE_FUJI = 0;
public static final int APPLE_PIPPIN = 1;
public static final int APPLE_GRANNY_SMITH = 2;
public static final int ORANGE_NAVEL = 0;
public static final int ORANGE_TEMPLE = 1;
public static final int ORANGE_BLOOD = 2;
타입 안전을 보장할 방법이 없고 표현력도 좋지 않다.
동등 연산자(==)로 값을 비교 할 수 없다.
값 자체가 숫자로만 보이기 때문에, 출력 및 디버거에서 크게 도움이 안된다.
namespace를 지원하지 않아, 위처럼 사과용 상수에는 'APPLE', 오렌지용 상수에는 'ORANGE'로 접두어를 사용하는 방식으로 값을 나누어야 한다.
Enum class
public enum Apple {FUJI, PIPPIN, GRANNY_SMITH, ORIGINAL}
public enum Orange {NAVEL, TEMPLE, BLOOD, ORIGINAL}
public abstract class Enum<E extends Enum<E>> ... {
private final String name;
public final String name() {
return name;
}
private final int ordinal;
@Range(from = 0, to = java.lang.Integer.MAX_VALUE)
public final int ordinal() {
return ordinal;
}
ordinal
값을 사용하던 곳에 모두 영향을 주게 된다.ordinal
값을 안쓰는 것을 추천한다 // 출력값 : APPLE:0 CHERRY:1 LEMON:2
public enum Juice {APPLE, CHERRY, LEMON}
// 출력값 : APPLE:0 CHERRY:1 WATERMELON:2 LEMON:3
public enum Juice {APPLE, CHERRY, WATERMELON, LEMON}
@Enumerated(EnumType.String)
를 사용하여 간편하게 name값을 사용 할 수 있다. @Column
@Enumerated(EnumType.String)
private TestType testType;
@Enumerated
의 기본 값은 EnumType.ORDINAL
이므로 반드시 변경하여 사용하자자바의 Enum은 메서드나 필드를 추가하고 인터페이스를 구현 할 수 있다.
public enum BasicOperation {
PLUS("+", (x, y) -> x + y) {
public double apply(double x, double y) { return x + y; }
},
MINUS("-", (x, y) -> x - y) {
public double apply(double x, double y) { return x - y; }
},
TIMES("*", (x, y) -> x * y) {
public double apply(double x, double y) { return x * y; }
},
DIVIDE("/", (x, y) -> x / y) {
public double apply(double x, double y) { return x / y; }
};
private final String symbol;
private final BiFunction<Integer, Integer, Integer> operation;
abstract double apply(double value, double value);
BasicOperation(String symbol, BiFunction<Integer, Integer, Integer> operation) {
this.symbol = symbol;
this.operation = operation;
}
public Integer operate(Integer x, Integer y) {
return this.operation.apply(x, y);
}
@Override public String toString() {
return symbol;
}
}
이러한 특징으로 아래와 같은 장점을 갖도록 구현 할 수 있다.
public enum TableStatus {
Y("1", true) // Y, "1", true가 같은 의미임을 쉽게 알 수 있다.
, N("0", false)
;
private String table1value;
private boolean table2value;
TableStatus(String table1value, boolean table2value) {
this.table1value = table1value;
this.table2value = table2value;
}
// table1value의 getter는 enum에 값이 추가된다고 코드가 추가되지 않는다.
public String getTable1value() {
TableStatus.Y.ordinal();
return table1value;
}
public class LegacyOperation {
public static int operate(String symbol, int x, int y) {
if("PLUS".equals(symbol)) {
return x + y;
} else if("MINUS".equals(symbol)) {
return x - y;
} else if("TIMES".equals(symbol)) {
return x * y;
} else if("DIVIDE".equals(symbol)) {
return x / y;
}
throw new IllegalArgumentException("Do not operate. Not found symbol.");
}
public static void main(String[] args) {
String symbol = findSymbol(); // 데이터는 데이터 대로 조회하고
int x = 100;
int y = 200;
int result = LegacyOperation.operate(symbol, x, y); // 계산은 별도의 메소드를 통해서 진행
}
public static void main(String[] args) {
BasicOperation operation = findOperation();
int x = 100;
int y = 200;
int result = operation.operate(x, y); // Enum에게 직접 계산을 요청
}
[그림 : 결제 종류 및 결제 수단 | 출처 : 우아한형제들 기술 블로그 Java Enum 활용기]
위의 그림과 같이 결제 데이터는 결제 종류와 결제 수단으로 표현 할 수 있다.
public String getPayGroup(String payCode) {
if("ACCOUNT_TRANSFER".equals(payCode) || "REMITTANCE".equals(payCode) || "ON_SITE_PAYMENT".equals(payCode) || "TOSS".equals(payCode)) {
return "CASH";
} else if ("PAYCO".equals(payCode) || "CARD".equals(payCode) || "KAKAO_PAY".equals(payCode) || "BAEMIN_PAY".equals(payCode)) {
return "CARD";
} else if ("POINT".equals(payCode) || "COUPON".equals(payCode)) {
return "ETC";
} else {
return "EMPTY";
}
}
public void pushByPayGroup(String payGroupCode) {
if("CASH".equals(payGroupCode)) {
pushCashMethod();
} else if("CARD".equals(payGroupCode)) {
pushCashMethod();
} else if("ETC".equals(payGroupCode)) {
pushCashMethod();
}
throw new RuntimeException("payGroupCode가 없습니다.");
}
public enum PayGroupAdvanced {
CASH("현금", Arrays.asList(PayType.ACCOUNT_TRANSFER, PayType.REMITTANCE, PayType.ON_SITE_PAYMENT, PayType.TOSS))
, CARD("카드", Arrays.asList(PayType.PAYCO, PayType.CARD, PayType.KAKAO_PAY, PayType.BAEMIN_PAY))
, ETC("기타", Arrays.asList(PayType.POINT, PayType.COUPON))
, EMPTY("없음", Collections.EMPTY_LIST)
;
private String title;
private List<PayType> payTypeList;
PayGroupAdvanced(String title, List<PayType> payTypeList) {
this.title = title;
this.payTypeList = payTypeList;
}
public static PayGroupAdvanced findByPayType(PayType payType) {
return Arrays.stream(PayGroupAdvanced.values())
.filter(payGroup -> payGroup.hasPayCode(payType))
.findAny()
.orElse(EMPTY);
}
private boolean hasPayCode(PayType payType) {
return payTypeList.stream()
.anyMatch(pay -> pay == payType);
}
}
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() {
System.out.println("기다려 자기야, 지금 나갈께!");
}
// 이 메서드는 보통 클래스 바깥(다른 클래스)에 작성해야 한다!
public static void main(String[] args) {
Elvis elvis = Elvis.INSTANCE;
elvis.leaveTheBuilding();
}
}
도대체 이 코드가 어디에서 쓰이는 것인지,
이 필드에는 어떤 값들만 허용 가능한 것인지,
A값과 B값이 실제로는 동일한 것인지,
전혀 다른 의미인지,이 코드를 사용하기 위해 추가로 필요한 메소드들은 무엇이고,
변경되면 어디까지 변경해야하는 것인지 등등불확실한 것들이 너무 많았던 상황에서 Enum을 통해 확실한 부분과 불확실한 부분을 분리할 수 있었습니다.
특히 가장 실감했던 장점은 문맥(Context)을 담는다는 것이였습니다.
Enum에 새로운 관점을 보게 해준 우아한형제들 기술 블로그 Java Enum 활용기 에 나온 내용입니다.
요약하는 과정에서 생략된 부분이 많으니, 이글을 읽어보신 분은 해당 글도 꼭 한번 읽어보시기 바랍니다.
감사합니다 :)