Enum Type에 대해서

2023. 11. 6. 03:01Java

이 글을 쓰게 된 계기

  EnumType이 되게 쉬운 거라고 생각해서 간단하게 생각하고 넘어갔는데 생각보다 기능도 있고 다양하게 사용할 수 있어서 해당 부분에 대해서 공부를 해보았다. 참고 사이트는 다음과 같다.

 

Enum Type을 쓰는 이유?

 

개인적인 생각이지만 클래스에서 변수값을 나타낼 게 여러 개인 경우 public static final int 같은 방식으로 상수를 지정할 수 있다. 그런데 그게 여러 개라면 해당 상수에 이름을 지정해주는 방법이 더 사용하기에 좋으니 관리하기 편하게 사용하기 위해서 사용하는 것이라고 생각을 한다.

 

 예를 들면, 원소 번호 같은 걸 들 수 있다. 원소 번호는 각각의 번호와 이름이 지정이 되어있는데 그걸 static으로 전부 다 나타내면 굉장히 코드가 보기 안 좋고 만약에 주기율표를 구현한다고 했을 때 반복이 일어날 수 있는 부분이니 그 부분을 정리할 수 있다.

 

public enum Element {
    // public static final int He = 1;
    // public static final int H = 2;
    // public static final int Li = 3;
    // public static final int Be = 4;
    // public static final int B = 5;

    He, H, Li, Be, B
}

 

 

그런데 뭔가 이 정도로는 부족합니다. 알아서 He, H 같은 원소에 번호를 붙여주는 것까지 그렇다고 치더라도, He의 원래 이름도 알고 싶고 He의 원자량도 저장을 하고 싶다. 그러면 여기에 필드를 붙여줄 수 있다. 

 

아래는 위의 문제가 예시 코드이고 예시 코드에는 해당 방식에서 사용할 때의 주의점에 대해서 주석으로 적어놓았다.

 

public enum Element {
    He("Hydrogen", 1.008),
    H("Helium", 4.002602),
    Li("Lithium", 6.94),
    Be("Beryllium", 9.0121831),
    B("Boron", 10.81);


    // 1. enum 자체에는 Enum.name() 메서드가 있기 때문에 label 같은 명을 추천
    // 2. 여기에서 final인 이유는 열거형의 필드에서의 값은 변경되지 않는다고 생각
    // 3. enum 자체에 new 연산자를 사용하는 게 되진 않지만 선언 목록에서 생성자 인수 전달 가능
    private final String label;
    private final double atomicWeight;

    private Element(String label, double atomicWeight){
        this.label = label;
        this.atomicWeight = atomicWeight;
    }

    public String label(){
        return label;
    }

    public double atomicWeight(){
        return atomicWeight;
    }

}

 

 

Value값으로 Enum 내부를 찾기

Java는 모든 enum 유형에 대해서 valueOf(String) 메소드를 제공한다. 그래서 선언된 이름을 통해서 열거형의 값을 얻을 수 있다.  그러면 위에 대해서는 두 가지 방법으로 조회해올 수 있는 것이다. label명을 통해서 혹은 atomicWeight를 통해서 조회가 가능하다. 

 

*** 각각의 코드 위의 한 줄을 통해서 나중에 테스트 코드로 확인을 할 수 있다.  ***

 

1. label 명을 통해서 조회해오기

    // Test: assertSame(Element.LI, Element.valueOfLabel("Lithium"));
    public static Element valueOfLabel(String label){
        for(Element e : values()){
            if(e.label.equals(label)){
                return e;
            }
        }
        return null;
    }

 

 

2. atomicWiehgt를 통해서 조회해오기

    // Test: assertSame(Element.LI, Element.valueOfAtomicWeight(1.008));
    public static Element valueOfAtomicWeight(double atomicWeight){
        for(Element e : values()){
            if(e.atomicWeight == atomicWeight){
                return e;
            }
        }
        return null;
    }

 

 

위보다 더 빠르게 조회하는 법

 위의 코드는 문제점이 for문을 통해서 전체 enum에서 선언된 부분을 순회하다가 찾아오게 된다. 그런데 저게 원소 같은 거여서 다행이었지 도서관의 책이나 상품 물건 수 같이 엄청나게 많다고 생각을 해보면 저 for문을 도는 것도 일이다. 그렇기 때문에 저 값을 캐싱을 할 수도 있다. 

 

HashMap 자료 구조를 <Value, Element> 형태로 놔두면 되는데 각각의 부분을 한 번 이런 형식으로 바꾸어보자. 그러면 아래와 같은 코드가 나오게 된다.

 

public enum Element {
    He("Hydrogen", 1.008),
    H("Helium", 4.002602),
    LI("Lithium", 6.94),
    Be("Beryllium", 9.0121831),
    B("Boron", 10.81);

    // HashMap을 통해서 캐싱을 하는 부분입니다.
    private static final Map<String, Element> BY_LABEL = new HashMap<>();
    private static final Map<Double, Element> BY_ATOMIC_WEIGHT = new HashMap<>();

    static{
        for(Element e : values()){
            BY_LABEL.put(e.label, e);
            BY_ATOMIC_WEIGHT.put(e.atomicWeight, e);
        }
    }

    private final String label;
    private final double atomicWeight;

    private Element(String label, double atomicWeight){
        this.label = label;
        this.atomicWeight = atomicWeight;
    }

    public static Element valueOfLabel(String label){
        return BY_LABEL.get(label);
    }

    public static Element valueOfAtomicWeight(double atomicWeight){
        return BY_ATOMIC_WEIGHT.get(atomicWeight);
    }
}

 

Enum을 쓸 때 주의사항

 enum을 쓸 때에는 두 가지 함수는 final로 지정되어있어서 변경할 수 없다.

 

1. valueOf() 메서드

2. name() 메서드

 

그렇기 때문에 해당 함수를 바꿀 수는 없으니 name()과는 다른 방식으로 나타내고 싶다면 toString을 바꾸라고 제시를 하고 있다.

 

toString() 함수 추가

    @Override
    public String toString(){
        return this.label;
    }

 

Main 함수에서 다음과 같이 코드를 놓았고 각각의 출력을 보자.

        String elementName = Element.LI.name();
        Element element = Element.valueOf(elementName);

        String elementName2 = Element.LI.toString();

        System.out.println("elementName: " + elementName);
        System.out.println("element: " + elementAtomicWeight);
        System.out.println("elementName2 : " + elementName2);

 

그러면 결과는 기존의 name과 valueOf처럼 두 줄로 해당 원소의 진짜 명칭을 알 수 있었는데 toString 한 줄로 간단하게 찾아올 수 있다. 

 

 

추가적으로 AtomicWeight도 한꺼번에 할 수 있는데 해당 부분은 toString() 함수를 다음과 같이 변경하면 된다.

    @Override
    public String toString(){
        return "Name: " + this.label + "\n" + "atomicWeight: " + this.atomicWeight;
    }

 

출력 결과는 직접 해보길 바란다. 인터페이스도 구현이 가능하다고는 하나 일반적이지 않다고 이야기하니 이 부분은 작성을 하지 않도록 하겠다.

 

참조 사이트: https://www.baeldung.com/java-enum-values

 

'Java' 카테고리의 다른 글

JVM, JRE, JDK의 차이가 무엇일까?  (0) 2023.12.28
Primitive Type과 Wrapper Class  (2) 2023.12.23
자바의 정석, 열거형(Enum) 2  (0) 2023.08.29
자바의 정석, 열거형(enums) 1  (0) 2023.08.07
자바의 정석, 지네릭스(Generics) 1  (0) 2023.08.07