열거한 값들이 단독이 아닌 집합으로 주로 사용될 경우 enum이 나오기 전에는 각 상수에 서로 다른 2의 거듭제곱 값을 할당한 정수 열거 패턴을 사용해 왔다.
책에서의 예제는 글자 클래스 안에 글자를 꾸며주는 상수들을 비트 연산자로 값을 할당하고 메서드를 사용할 때도 비트 연산자를 사용하고 있다.
이렇게 단독으로 사용도 하겠지만 서로 연관되어 사용하기 위해 만든 상수들을 집합이라 칭하는 것 같고 비트 연산자 값을 사용하여 선언한 상수의 집합을 비트 필드라 칭하는 것 같다.
각 상수는 왼쪽 시프트 연산자를 사용해서 2의 거듭제곱 형태의 값을 배정받고
main 메서드에서 상수가 단독적으로 사용되는 것이 아닌 OR(합집합) 으로 매개변수를 전달하고 있다.
글자 스타일을 굵고 기울게 나타낼 것이라는 의미이고
STYLE_BOLD는 0001(1), STYLE_ITALIC은 0010(2) 으로 합집합하면 0011(3)이 된다.
비트 필드를 사용하면 비트별 연산을 사용해 합집합과 교집합 같은 집합 연산을 효율적으로 수행할 수 있다.
2의 거듭제곱의 값은 각 상수가 어떻게 합집합이 되어도 상수에 중복이 되는 숫자가 없어 유니크한 값이 되어 정수 열거 패턴에 사용하기 좋은 방법이라 할 수 있다.
하지만 비트 필드는 정수 열거 상수의 단점을 그대로 지니며 추가로 다음과 같은 문제를 안고 있는데
- 비트 필드 값이 그대로 출력되면 단순한 정수 열거 상수를 출력할 때 보다 해석하기 훨씬 어렵다.
출력된 "Appliying styles 3 to text" 이 글자 굵게와 기울기를 나타낸다는 의미를 알기 어렵다. - 비트 필드 하나에 녹아 있는 모든 원소를 순회하기도 까다롭다.
별도로 집합으로 이루어진 상수들만 순회하는 로직을 별도로 구현해야 한다. - 최대 몇 비트가 필요한지 API 작성시 미리 예측하여 적절한 타입(보통 int나 long)을 선택해야 한다. API를 수정하지 않고 비트 수(32비트 or 64비트)를 더 늘릴 수 없기 때문이다.
처음 개발시 갯수가 특정되어도 추후 상수가 얼마나 늘어날 지 알 수 없고 상수가 long 으로 만들었다고 해도 64비트만 지원하기 때문에 상수가 65개 이상 넘어가게 된다면 더 늘릴 수 없다.
이런 문제를 해결하기 위해 java.util 패키지의 EnumSet 클래스는 열거 타입 상수의 값으로 구성된 집합을 효과적으로 표현해준다.
Set 인터페이스를 완벽히 구현하며, 타입 안전하고, 다른 어떤 Set 구현체와도 함께 사용할 수 있다.
위의 정수 열거 패턴의 예시를 EnumSet으로 수정하면
EnumSet은 집합 생성 등 다양한 기능의 정적 팩터리를 제공하는데 of 메서드를 사용하여 EnumSet을 생성하였다.
출력된 코드도 "Appliying styles [BOLD, ITALIC] to text" 명시적으로 어떠한 글자 꾸밈이 추가되었는지 가독성도 좋다.
applyStyles 메서드에서 EnumSet<Style> 파라미터를 받은게 아닌 Set<Style>을 받은 이유는 이왕이면 인터페이스로 받는게 일반적으로 좋은 습관이고 이렇게 하면 특이한 클라이언트가 다른 Set 구현체를 넘기더라도 처리할 수 있다.
참고
'개인룸 > 도윤' 카테고리의 다른 글
Item38. 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라 (0) | 2023.05.23 |
---|---|
Item37. ordinal 인덱싱 대신 EnumMap을 사용하라 (0) | 2023.05.21 |
Item35. ordinal 메서드 대신 인스턴스 필드를 사용하라 (1) | 2023.05.16 |
Item34. int 상수 대신 열거 타입을 사용하라 (0) | 2023.05.14 |
Item33. 타입 안전 이종 컨테이너를 고려하라 (0) | 2023.04.24 |