본문 바로가기
카테고리 없음

Java - enum 활용 가이드

by Nomangs 2022. 7. 20.
반응형

오늘은 Java 에서 은근히 많이 활용하는 Enum 을 소개합니다.

 

개요

Java 5는 처음으로 enum 키워드를 도입했습니다. 항상 java.lang.Enum 클래스 를 확장하는 특수한 유형의 클래스를 나타냅니다 . 

이러한 방식으로 정의된 상수는 코드를 더 읽기 쉽게 만들고, 컴파일 시간 확인을 허용하고, 허용되는 값 목록을 미리 문서화하고, 잘못된 값이 전달되어 예기치 않은 동작을 방지합니다.


다음은 피자 주문 상태를 정의하는 열거형의 빠르고 간단한 예입니다. 주문 상태는 ORDERED , READY 또는 DELIVERED 일 수 있습니다 .

public enum PizzaStatus {
    ORDERED,
    READY, 
    DELIVERED; 
}

 

사용자 지정 열거형 메서드

열거형이 무엇이고 어떻게 사용할 수 있는지에 대한 기본적인 이해를 하였으므로 열거형에 대한 몇 가지 추가 API 메서드를 정의하여 이전 예제를 다음 단계로 넘어갈 것입니다.

public class Pizza {
    private PizzaStatus status;
    public enum PizzaStatus {
        ORDERED,
        READY,
        DELIVERED;
    }

    public boolean isDeliverable() {
        if (getStatus() == PizzaStatus.READY) {
            return true;
        }
        return false;
    }
    
    // Methods that set and get the status variable.
}

 

"==" 연산자를 사용한 열거형 비교

열거형 유형은 JVM에 하나의 상수 인스턴스만 존재하도록 보장하므로 위의 예에서와 같이 "==" 연산자를 사용하여 두 변수를 안전하게 비교할 수 있습니다. 또한 "==" 연산자는 컴파일 시간 및 런타임 안전성을 제공합니다.

먼저 다음 스니펫 에서 런타임 안전성 을 살펴 보겠습니다. 여기서 "==" 연산자를 사용하여 상태를 비교합니다. 두 값 모두  null 일 수 있으며 NullPointerException이 발생 하지 않습니다  . 반대로 equals 메서드를 사용하면 NullPointerException 이 발생합니다 .

if(testPz.getStatus().equals(Pizza.PizzaStatus.DELIVERED)); 
if(testPz.getStatus() == Pizza.PizzaStatus.DELIVERED);

컴파일 시간 안전성 에 관해서 는 equals 메서드 를 사용하여 비교하여 다른 유형의 열거형이 동일한지 확인하는 예제를 살펴보겠습니다 . enum과 getStatus 메소드의 값이 우연히 동일하기 때문입니다. 그러나 논리적으로 비교는 거짓이어야 합니다. "==" 연산자를 사용하여 이 문제를 방지합니다.

컴파일러는 비교를 비호환성 오류로 플래그 지정합니다.

if(testPz.getStatus().equals(TestColor.GREEN));
if(testPz.getStatus() == TestColor.GREEN);

 

Switch 문에서 열거형 사용하기

switch 문 에서 열거형 유형을 사용할 수도 있습니다.

public int getDeliveryTimeInDays() {
    switch (status) {
        case ORDERED: return 5;
        case READY: return 2;
        case DELIVERED: return 0;
    }
    return 0;
}

 

열거형의 필드, 메서드 및 생성자

열거형 내부에 생성자, 메서드 및 필드를 정의할 수 있으므로 매우 강력합니다.

다음으로 피자 주문의 한 단계에서 다른 단계로의 전환을 구현하여 위의 예를 확장해 보겠습니다. 이전에 사용된 if 및 switch 문 을 제거하는 방법을 살펴보겠습니다 .

public class Pizza {

    private PizzaStatus status;
    public enum PizzaStatus {
        ORDERED (5){
            @Override
            public boolean isOrdered() {
                return true;
            }
        },
        READY (2){
            @Override
            public boolean isReady() {
                return true;
            }
        },
        DELIVERED (0){
            @Override
            public boolean isDelivered() {
                return true;
            }
        };

        private int timeToDelivery;

        public boolean isOrdered() {return false;}

        public boolean isReady() {return false;}

        public boolean isDelivered(){return false;}

        public int getTimeToDelivery() {
            return timeToDelivery;
        }

        PizzaStatus (int timeToDelivery) {
            this.timeToDelivery = timeToDelivery;
        }
    }

    public boolean isDeliverable() {
        return this.status.isReady();
    }

    public void printTimeToDeliver() {
        System.out.println("Time to delivery is " + 
          this.getStatus().getTimeToDelivery());
    }
    
    // Methods that set and get the status variable.
}

아래의 테스트 스니펫은 이것이 어떻게 작동하는지 보여줍니다

@Test
public void givenPizaOrder_whenReady_thenDeliverable() {
    Pizza testPz = new Pizza();
    testPz.setStatus(Pizza.PizzaStatus.READY);
    assertTrue(testPz.isDeliverable());
}

 

자바 8과 열거형

Java 8에서 Pizza 클래스를 다시 작성할 수 있으며 람다 및 Stream API 를 사용하여 getAllUndeliveredPizzas() 및 groupPizzaByStatus( ) 메서드가 얼마나 간결해졌는지 확인할 수 있습니다.

public static List<Pizza> getAllUndeliveredPizzas(List<Pizza> input) {
    return input.stream().filter(
      (s) -> !deliveredPizzaStatuses.contains(s.getStatus()))
        .collect(Collectors.toList());
}
public static EnumMap<PizzaStatus, List<Pizza>> 
  groupPizzaByStatus(List<Pizza> pzList) {
    EnumMap<PizzaStatus, List<Pizza>> map = pzList.stream().collect(
      Collectors.groupingBy(Pizza::getStatus,
      () -> new EnumMap<>(PizzaStatus.class), Collectors.toList()));
    return map;
}

Enum의 JSON 표

Jackson 라이브러리를 사용하면 POJO인 것처럼 열거형 유형의 JSON 표현을 가질 수 있습니다. 아래 코드 스니펫에서 동일한 항목에 대해 Jackson 주석을 사용하는 방법을 확인할 수 있습니다.

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum PizzaStatus {
    ORDERED (5){
        @Override
        public boolean isOrdered() {
            return true;
        }
    },
    READY (2){
        @Override
        public boolean isReady() {
            return true;
        }
    },
    DELIVERED (0){
        @Override
        public boolean isDelivered() {
            return true;
        }
    };

    private int timeToDelivery;

    public boolean isOrdered() {return false;}

    public boolean isReady() {return false;}

    public boolean isDelivered(){return false;}

    @JsonProperty("timeToDelivery")
    public int getTimeToDelivery() {
        return timeToDelivery;
    }

    private PizzaStatus (int timeToDelivery) {
        this.timeToDelivery = timeToDelivery;
    }
}

다음과 같이 Pizza 및 PizzaStatus 를 사용할 수 있습니다 .

Pizza pz = new Pizza();
pz.setStatus(Pizza.PizzaStatus.READY);
System.out.println(Pizza.getJsonString(pz));

그러면 Pizza 상태에 대한 다음 JSON 표현이 생성됩니다.

{
  "status" : {
    "timeToDelivery" : 2,
    "ready" : true,
    "ordered" : false,
    "delivered" : false
  },
  "deliverable" : true
}

열거형 유형의 JSON 직렬화/역직렬화(사용자 정의 포함)에 대한 자세한 내용은 Jackson – 직렬화 열거형을 JSON 개체로 참조할 수 있습니다 .

 

반응형

댓글