[Effective Java 3/E] ITEM 36. 비트 필드 대신 EnumSet을 사용하라

    Effective Java 표지. Source : https://blog.insightbook.co.kr/

    작성자 주: 저는 C/C++ 개발을 할 때 비트 필드 구현을 많이 사용했습니다. 하나의 int/long 타입 정수에 여러 개의 flag를 넣을 수 있어 많이 사용됩니다. 보통 or 연산으로 합치고, and로 원하는 flag를 가져오는 식으로 사용합니다.

    아래 예제는 리눅스 커널의 mman.h 파일에 있는 flag의 일부입니다.

     

    #ifdef __USE_MISC
    # define MAP_GROWSDOWN        0x00100                /* Stack-like segment.  */
    # define MAP_DENYWRITE        0x00800                /* ETXTBSY */
    # define MAP_EXECUTABLE        0x01000                /* Mark it as an executable.  */
    # define MAP_LOCKED        0x02000                /* Lock the mapping.  */
    # define MAP_NORESERVE        0x04000                /* Don't check for reservations.  */
    # define MAP_POPULATE        0x08000                /* Populate (prefault) pagetables.  */
    # define MAP_NONBLOCK        0x10000                /* Do not block on IO.  */
    # define MAP_STACK        0x20000                /* Allocation is for a stack.  */
    #endif

     

    비트 필드의 단점은 정수 열거 패턴의 단점을 그대로 지닙니다. (ITEM 34)

     

    참고하기 :

    1. 이 정수 열거 패턴은 type safe를 보장할 수 없다.(APPLE_FUJI==ORANGE_NAVEL의 결과는?)
    2. 개발자의 의도는 APPLE과 ORANGE를 분리하여 사용하고자 했을 것이다. namespace를 컴파일러가 아닌 개발자에게 맡겨버렸다.
    3. 컴파일하면 클라이언트 파일에 그대로 새겨진다.
    4. 상수의 값을 바꾸려면 다시 컴파일해야 했다.
    5. 디버깅할 때 의미나 문자열이 아닌 숫자로 보이기 때문에 디버그하기 힘들다.
    6. 순회를 하기 힘들다. 해당 가상의 namespace 안에 몇 개의 상수가 있을까?

     

    또한

    • 최대 몇 비트를 사용할 것인지 int나 long으로 적절히 선택해야 하며, 이미 확정되어 구현된 이후로는 의존성 때문에 수정이 매우매우 힘들다. 64bit가 최대인 한계도 존재한다.

     

    대안 : EnumSet 클래스 사용하기

     

    EnumSet의 특징은

    1. Set 인터페이스를 구현했다.
    2. 타입 안전하다.
    3. 원소가 64개 이하인 경우 비트 벡터 구현을 이용하여 비트 필드 수준의 성능을 보여준다.
    4. 직접 비트를 다룰 때의 오류를 걱정하지 않아도 된다.
    public class Text {
        public enum Style {BOLD, ITALIC, UNDERLINE, STRIKETHROUGH}
    
        // 어떤 Set을 넘겨도 되나, EnumSet이 가장 좋다.
        public void applyStyles(Set<Style> styles) {
            System.out.printf("Applying styles %s to text%n",
                    Objects.requireNonNull(styles));
        }
    
        // 사용 예
        public static void main(String[] args) {
            Text text = new Text();
            text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
        }
    }

     

    불변 EnumSet을 만들 수 없다는 단점이 있지만 비트 필드를 사용할 이유가 없어졌다. EnumSet을 활용하자.

    댓글