핵심 요약
열거 타입 상수의 집합을 표현할 때, 과거에 사용되던 비트 필드(Bit Field) 방식 대신 현대 자바에서는 EnumSet
을 사용하는 것이 훨씬 안전하고 효율적이며 가독성 높은 방법입니다.
EnumSet
은 타입 안전성을 보장하고 비트 필드 수준의 성능을 제공하면서도 사용하기 쉽습니다.
비트 필드
정의
- 열거한 값들을 주로 집합으로 사용할 경우, 각 상수에 서로 다른 2의 거듭제곱 값을 할당한 정수 열거 패턴을 사용
- 비트별 OR(
|
) 연산을 통해 여러 상수를 하나의 집합(정수 값)으로 표현
예시 코드
public class FilePermissions {
public static final int PERMISSION_READ = 1 << 0; // 1 (읽기 권한)
public static final int PERMISSION_WRITE = 1 << 1; // 2 (쓰기 권한)
public static final int PERMISSION_EXECUTE = 1 << 2; // 4 (실행 권한)
public void setPermissions(int permissions) {...}
}
// 상수 정의 (정수 값 사용)
public class Text_BitField {
public static final int STYLE_BOLD = 1 << 0; // 1
public static final int STYLE_ITALIC = 1 << 1; // 2
public static final int STYLE_UNDERLINE = 1 << 2; // 4
public static final int STYLE_STRIKETHROUGH = 1 << 3; // 8
// 비트 필드(int)를 받는 메서드
public void applyStyles(int styles) {
System.out.println("Applying styles (bitmask): " + styles);
// ... 스타일 적용 로직 ...
}
}
// 사용 예시
text.applyStyles(STYLE_ITALIC | STYLE_UNDERLINE); // 값은 6
fp.setPermissions(STYLE_ITALIC | STYLE_UNDERLINE); // 오류없음
특징
- 비트별 연산(OR, AND 등)을 사용하여 합집합, 교집합 등의 집합 연산을 효율적으로 수행 가능 (과거 관점).
장점
- (과거 기준) 비트 연산을 통한 효율적인 집합 연산.
단점
- 타입 안전성 부재
- 단순
int
값이므로 관련 없는 값을 전달해도 컴파일 오류 미발생
- 단순
- 가독성 저하
- 숫자만으로 스타일 정보 추론?
- 순회 어려움
- 집합에 포함된 개별 원소(스타일)를 순회하기 까다로움
- 확장성 제한
- 최대 원소 개수를 미리 예측하여
int
나long
타입을 선택 - 비트 수 확장할 때 API 변경이 필요
- 최대 원소 개수를 미리 예측하여
- 오류 가능성
- 비트 연산은 직접 다루기 까다롭고 오류가 발생 가능성
EnumSet
정의
- 열거 타입(Enum) 상수들로 구성된 집합을 표현하기 위해 특별히 설계된
Set
인터페이스의 고성능 구현체 - Set 인터페이스를 완벽히 구현, 타입 안전하고, 다른 어떤 Set 구현체와도 함께 사용 가능
- 비트 필드를 대체하는 현대적이고 우수한 대안
java.util
패키지
예시 코드
import java.util.EnumSet;
import java.util.Set;
// 열거 타입 정의
public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH }
public class Text_EnumSet {
// Set 인터페이스로 받는 것이 좋음
public void applyStyles(Set<Style> styles) {
System.out.println("Applying styles (EnumSet): " + styles);
// ... 스타일 적용 로직 ...
}
}
// 사용 예시
Text_EnumSet text = new Text_EnumSet();
text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC)); // 출력: [BOLD, ITALIC]
특징
- 내부적으로 비트 벡터를 사용하여 데이터를 저장
- 비트 벡터는 주로 단일
long
변수 - 매우 효율적
- 비트 벡터는 주로 단일
Set
인터페이스를 완벽하게 구현하며, 다른Set
구현체와 함께 사용 가능- 다양한 정적 팩터리 메서드를 제공하여 편리한 인스턴스 생성
EnumSet.of()
,EnumSet.allOf()
,EnumSet.noneOf()
등
장점
- 타입 안전성
- 정의된 열거 타입의 상수만 포함
- 컴파일 시점에 타입 오류를 방지
- 가독성
- 코드가 명확, 직관적
- 출력 예시
[BOLD, ITALIC]
- 고성능
- 내부 구현 방식 덕분에 비트 필드에 비견될 만한 성능을 제공
- 대량 작업도 비트 연산을 활용해 효율적으로 처리
- removeAll, retainAll 등
- 사용 편의성
- 복잡한 비트 연산 내부 구현
- 안전하고 편리한 집합 연산 제공
- 유지보수 용이
- 열거 타입 상수 추가시
EnumSet
코드는 대부분 수정 없이 작동
- 열거 타입 상수 추가시
단점
- (과거) 불변
EnumSet
을 직접 생성 불가 (Java 9 이전)- 현재는 해결
핵심 정리 및 부가 정보
핵심 정리
결론적으로, 열거 타입 상수의 집합을 사용할 때는 명료성, 성능, 타입 안전성, 유지보수성 등 모든 면에서 비트 필드 대신
EnumSet
을 사용하는 것이 압도적으로 좋습니다.
API 설계 팁:
// 구체화 EnumSet<E> -> 인터페이스 Set<E>
public void applyStyles(Set<Style> styles) // EnumSet ?
EnumSet<E>
대신Set<E>
을 사용하는 것이 더 유연하고 좋은 설계- 클라이언트가 다른
Set
구현체를 사용할 가능성
불변성(Immutability) 처리:
Collections.unmodifiableSet(EnumSet.of(...))
- Java 9 이전에는 불변
EnumSet
을 만드는 표준적인 방법이 없어서 사용 - 원본
EnumSet
이 변경되면 같이 변경될 수 있는 '뷰'를 제공할 뿐 - 명확성, 성능 조금 희생
- Java 9 이전에는 불변
Set.copyOf(enumSet)
- Java 10 이후 도입
- 해당
EnumSet
의 내용을 복사하여 진정한 불변Set
을 생성 - 더 안전하고, 특히
EnumSet
의 경우 내부적으로 최적화되어 성능상 이점
'Programming Language > Java' 카테고리의 다른 글
[EFFECTIVE JAVA] ordinal 인덱싱 대신 EnumMap을 사용하라 (0) | 2025.03.29 |
---|---|
[EFFECTIVE JAVA] 한정적 와일드 카드를 사용해 API 유연성을 높이라 (0) | 2025.03.17 |
비한정적 와일드 카드 & 한정적 와일드 카드 (0) | 2025.03.17 |
공변성 & 반공변성 & 불공변성? (0) | 2025.03.17 |
[EFFECTIVE JAVA] Comparable을 구현할지 고려하라 (0) | 2025.03.12 |