서론
type이나 상태값을 나타내기위해 많은분들이 enum을 사용합니다.
enum을 활용하는 방법, 사용할때 알아두면 좋은 팁들을 정리해보려합니다.
예제
아래는 설명을 위해 사용할 예제입니다. 본문에 계속 사용될 예정입니다.
주문의 상태를 나타내는 enum입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public enum OrderStatus { ORDER("주문"), PAYMENT("결제완료"), DELIVERY("배송"), REJECT("거절"), REFUNDED("환불"), CANCEL("취소");
private String description;
TicketStatus(String description) { this.description = description; } }
|
enum 기본기능 활용
name()
name()
를 통해 이름을 찾을 수 있습니다
이름을 따로 맴버변수로 등록할 필요가 없습니다.
1 2
| String name = OrderStatus.DELIVERY.name(); log.debug("name : {}", name);
|
output :
values()
Enum들을 모두 포함하고있는 배열을 얻을 수 있는 방법도 있습니다.
1
| OrderStatus[] values = OrderStatus.values();
|
Java Collection의 List로 사용하고자 하신다면 Arrays.asList()
를 사용하시면 됩니다.
stream등 collection의 기능을 활용하기 편리해집니다.
1
| List<OrderStatus> orderStatuses = Arrays.asList(OrderStatus.values());
|
find by name
String으로 enum값을 찾는경우입니다.
String으로 된 변수에 이름이 들어가있고. 이 이름으로 enum값을 찾아야하는 경우가 있습니다.
이럴때는 매우 다양한 방법으로 구현할수 있는데요.
valueOf()
대표적으로는 enum의 기본기능을 활용하는 방법입니다.
valueOf()
는 String으로 enum값을 찾아주는 대표적인 방법입니다.
1 2 3 4 5 6 7 8
| public static OrderStatus trycatchValueOf(String name) try { return OrderStatus.valueOf(name); } catch (Exception ex) { log.warn("Exception Thrown", ex); return null; } }
|
NullPointerException
가발생하거나, 의도하지 않은 문자열인경우에 IllegalArgumentException
이 발생하므로 try-catch
구문이 필요합니다. try-catch
을 로직의 플로우로 사용하는것은 지양
하기때문에 이방법을 추천드리지는 않습니다.
iterator 사용
또한 valueOf()
의 경우 내부적으로 iterator
를 사용하기때문에 모든항목을 하나씩 비교합니다. 때문에 O(n)
의 시간복잡도를 가진다는 단점이 있습니다.
1 2 3 4 5 6 7 8
| public static OrderStatus iterationFindByName(String name) { for (OrderStatus status : OrderStatus.values()) { if (name.equals(status.name())) { return status; } } return null; }
|
시간복잡도를 줄이기위해서는 static HashMap
을 미리 구성해놓는 대안이 있습니다.
[Guava] Enums.getIfPresent()
Google 에서만든 Guava
라이브러리는 우리가 원하는 기능을 완벽하게 지원하는데요
아래와같이 사용 할 수 있습니다
1 2 3
| public static OrderStatus getIfPresent(String name) { return Enums.getIfPresent(OrderStatus.class, name).orNull(); }
|
Json
아무런 설정을 하지않는다면 JsonFormat.Sahpe.String 으로 설정되어 되어있다면 아래와같이 출력되는데요.
1 2 3
| { orderStatus : CANCEL }
|
api의 response를 통해 전송할때 json으로 변환하는경우가 습니다. description등 enum의 맴버변수까지 접근하길 원한다면 object형태로 전송할수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12
| @JsonFormat(shape = JsonFormat.Shape.OBJECT) ... public enum OrderStatus { ORDER("주문"), PAYMENT("결제완료"), ...; private String description; public String getDescription(){ return this.description; } }
|
output :
1 2 3 4 5
| { orderStatus : { description : '취소' } }
|
enum의 name까지 필요한경우는 getter를 만들어줍니다.
JsonFormat.Shape.OBJECT
에서 OBJECT
를 만드는 기준은 getter입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @JsonFormat(shape = JsonFormat.Shape.OBJECT) public enum OrderStatus { ORDER("주문"), PAYMENT("결제완료"), ...; private String description; public String getDescription(){ return this.description; } String getName() { return this.name(); }
}
|
output :
1 2 3 4 5 6
| { orderStatus : { name: 'CANCEL' description : '취소' } }
|
비교
equals() 로 비교할 필요 없이 ==
로 비교하면 정상적으로 비교될 뿐 아니라 더 효율적입니다.
enum 은 singleton으로 생성되기때문에 어느곳에서 비교하더라도 값이 동등
할 뿐 아니라 동일
하게 취급되기 때문입니다.
변수로 람다 지정하기
enum값에 따라 처리방법이 다르기때문에 분기처리하여 작업하는 경우가 자주 일어납니다.
경우에 따라서는 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
| enum Operation implements DoubleBinaryOperator { PLUS("+") { @Override public double applyAsDouble(final double left, final double right) { return left + right; } }, MINUS("-") { @Override public double applyAsDouble(final double left, final double right) { return left - right; } }, MULTIPLY("*") { @Override public double applyAsDouble(final double left, final double right) { return left * right; } }, DIVIDE("/") { @Override public double applyAsDouble(final double left, final double right) { return left / right; } };
private final String symbol;
private Operation(final String symbol) { this.symbol = symbol; }
public String getSymbol() { return symbol; } }
|
람다를 쓴다면 가독성을 훨신 좋게 만들 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| enum Operation implements DoubleBinaryOperator { PLUS ("+", (l, r) -> l + r), MINUS ("-", (l, r) -> l - r), MULTIPLY("*", (l, r) -> l * r), DIVIDE ("/", (l, r) -> l / r);
private final String symbol; private final DoubleBinaryOperator binaryOperator;
private Operation(final String symbol, final DoubleBinaryOperator binaryOperator) { this.symbol = symbol; this.binaryOperator = binaryOperator; }
public String getSymbol() { return symbol; }
@Override public double applyAsDouble(final double left, final double right) { return binaryOperator.applyAsDouble(left, right); } }
|
분류
특정 주문이 취소 가능한지 체크하는 로직이 있다고 가정해 봅시다.
enum으로 선언된 type으로 구별하려면 아래와 같이 할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public enum OrderStatus { ORDER("주문"), PAYMENT("결제완료"), DELIVERY("배송"), REJECT("거절"), REFUNDED("환불"), CANCEL("취소"); public static final List<OrderStatus> CANCELABLE_STATUS = Arrays.asList(ORDER,PAYMENT);
public static boolean isCancelableStatus(OrderStatus status){ return CANCELABLE_STATUS.contains(status); } }
|
참고자료
https://stackoverflow.com/questions/23361418/lambdas-in-the-classical-operation-enum-example
https://www.javacodex.com/ENUMs/Calculator-using-enums-for-operations