Java

[Effective Java] 6-1. 열거 타입과 애노테이션

noahkim_ 2024. 12. 30. 22:22

조슈아 블로크 님의 "Effective Java" 책을 정리한 포스팅 입니다.


1. int 상수 대신 열거 타입을 사용하라

정수 열거 타입 vs Enum

항목 정수 열거 타입 (int enum pattern) Enum (자바 열거 타입)
타입 안정성 ❌ 잘못된 값도 전달될 수 있음 ✅ 컴파일 타임에 잘못된 값 차단 가능
이름 공간 ❌ 상수 이름이 전역으로 퍼짐 ✅ 열거 타입 별도로 이름공간을 가짐
유지보수 상수 값 변경 시, 모든 클라이언트 코드 수정 필요 상수 변경이 있어도 클라이언트 코드는 영향 거의 없음
순회 (iterability) 수동 작업 필요 (별도 배열 등) 간단하게 순회 가능 (Enum.values())
상수 개수 파악 어렵거나 불명확함 간단하게 확인 가능 (values().length)
필드/메서드 추가 가능 여부  단순 int 값일 뿐 ✅ 필드, 메서드, 생성자 등 클래스 수준 기능 활용 가능
인터페이스 구현 가능 여부 ✅ 각 열거 상수가 특정 동작을 구현하도록 만들 수 있음
확장성 (상속 등)  단순 상수 집합일 뿐  열거 타입의 생성자는 기본적으로 private임
보안 및 안정성 낮음 – 정수 값이 예상 밖의 결과 유발 가능 높음 – 엄격한 타입 제한과 불변성 보장

 

예시) 정수 열거 타입

더보기
public final class IntEnumPattern {
    public static final int APPLE_FUJI = 0;
    public static final int APPLE_PIPPIN = 1;
    public static final int APPLE_GRANNY_SMITH = 2;

    public static final int ORANGE_NAVEL = 0;
    public static final int ORANGE_TEMPLE = 1;
    public static final int ORANGE_BLOOD = 2;
}

 

예시) Enum

더보기
// 인터페이스 정의
interface Displayable {
    String display();
}
// Enum 정의
enum Fruit implements Displayable {
    APPLE("Red", 95) {
        @Override
        public String specialFeature() {
            return "Keeps doctors away!";
        }
    },
    ORANGE("Orange", 62) {
        @Override
        public String specialFeature() {
            return "Rich in Vitamin C!";
        }
    },
    BANANA("Yellow", 105) {
        @Override
        public String specialFeature() {
            return "Great for energy boost!";
        }
    };

    // 필드
    private final String color;
    private final int calories;

    // 생성자
    Fruit(String color, int calories) {
        this.color = color;
        this.calories = calories;
    }

    // 일반 메서드
    public String getColor() { return color; }
    public int getCalories() { return calories; }
        
    // 추상 메서드: 각 열거 상수가 고유한 동작을 제공
    public abstract String specialFeature();

    // 인터페이스 메서드 구현
    @Override
    public String display() {
        return String.format("%s: Color=%s, Calories=%d", this.name(), color, calories);
    }
}

 

2. ordinal 메서드 대신 인스턴스 필드를 사용하라

ordinal 메서드

  • 열거 타입에서 몇번째 인스턴스인지를 반환하는 메서드
  • 열거타입 기반의 범용 자료구조에 쓸 목적으로 설계됨
  • 유지보수 하기 어려움

 

ordinal 관련 값은 인스턴스 필드에 저장하기

public enum Ensemble {
    SOLO(1), DUET(2);
    
    private final int numberOfMusicians;
}

 

 

3. 비트 필드 대신 EnumSet을 사용하라

비트 필드

public class Text {
    public static final int STYLE_BOLD = 1 << 0;      // 0001
    public static final int STYLE_ITALIC = 1 << 1;   // 0010
    public static final int STYLE_UNDERLINE = 1 << 2; // 0100
    public static final int STYLE_STRIKETHROUGH = 1 << 3; // 1000

    public void applyStyles(int styles) {
        System.out.println("Applying styles: " + styles);
    }

    public static void main(String[] args) {
        Text text = new Text();
        text.applyStyles(STYLE_BOLD | STYLE_ITALIC); // 비트 연산 사용
    }
}
  • 여러 상수 값을 조합해서 사용할 수 있도록 비트를 활용한 방식

 

단점
  • 타입 안전성 부족
  • 가독성 낮음
  • 확장성 부족

 

 

EnumSet

public class Text {
    public enum Style {
        BOLD, ITALIC, UNDERLINE, STRIKETHROUGH
    }

    public void applyStyles(Set<Style> styles) {
        System.out.println("Applying styles: " + styles);
    }

    public static void main(String[] args) {
        Text text = new Text();
        text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC)); // EnumSet 사용
    }
}
  • 열거 타입 상수의 값으로 구성된 집합을 효과적으로 표현해줌
  • Set 인터페이스 구현
  • 내부는 비트 벡터로 구현됨

 

장점
  • 타입 안전