IT/Programming / / 2023. 4. 27. 09:22

<Effective Java> RULE 33 ordinal을 배열첨자로 사용하는 대신 EnumMap을 이용하라

반응형

 

public class Herb {
    enum Type{ ANNUAL, PERENNIAL, BIENNIAL }

    final String name;
    final Type type;

    Herb(String name, Type type){
        this.name = name;
        this.type = type;
    }

    @Override
    public String toString() {
        return name;
    }
}
 
 
//ordinal 값을 배열 첨자로 이용하는 방법
//지양
class HerbTest1{
    Herb[] garden;
    
    public void test1() {
        Set<Herb>[] herbsByType = 
                (Set<Herb>[]) new Set[Herb.Type.values().length];
                                //Indexed by Herb.Type.ordinal()
        for( int i = 0 ; i < herbsByType.length ; i++) {
            herbsByType[i] = new HashSet<Herb>();
        }
        
        for( Herb h : garden ) {
            herbsByType[h.type.ordinal()].add(h);
        }
        
        //결과 출력
        for ( int i = 0 ; i < herbsByType.length ; i++) {
            System.out.println(Herb.Type.values()[i] +" "+ herbsByType[i]);
        }
    }
}
 

· 배열을 제네릭과 호환되지 않으므로(규칙 25) 배열을 사용하려면 무점검 형변환해야한다.

· 배열은 무엇을 나타내는지 모르므로 라벨과 같이 출력해야한다.

· ordinal값의 Enum만큼의 int는 형안전성이 보장되지 않는다.

· 틀린 값을 쓰면 ArrayIndexOutOfBoundsException예외가 발생한다.

//EnumMap을 사용해 enum상수별 데이터를 저장하는 프로그램
class HerbTest2{
    Herb[] garden;

    public void test1() {
        Map<Herb.Type, Set<Herb>> herbsByType =
           new EnumMap<Herb.Type , Set<Herb>>(Herb.Type.class);
                          //한정적 자료형 토큰 : 실행시점 제네릭 자료형 정보를 제공 (규칙 29)                                                  
        
        for(Herb.Type t : Herb.Type.values()) {
            herbsByType.put(t, new HashSet<Herb>());
        }
        
        for(Herb h : garden) {
            herbsByType.get(h.type).add(h);
        }
        
        //결과 출력
        System.out.println(herbsByType);
    }
}
 

· Enum상수를 키로 사용할 목적으로 설계된 Map : EnumMap

· 무점검 형변환, 레이블을 만들 필요가 없다.

ordinal을 두개를 사용하여 enum상수와의 관계를 표현하는 코드

//상전이 예제 클래스
enum Phase{ 
    SOLID, LIQUID, GAS;
    
    public enum Transition{
        MELT, FREEZE, BOIL, CONDENSE, SUBLIME, DEPOSIT;
        
        //아래 배열의 행은 상전이 이전상태를 나타내는 enum 상수의 ordinal값을 첨자로 사용하고
        //열은 상전이 이후 상태를 나타내는 enum상수의 ordinal값을 첨자로 사용한다.
        private static final Transition[][] TRANSITTIONS = {
                {null, MELT, SUBLIME},
                {FREEZE,null,BOIL},
                {DEPOSIT, CONDENSE, null}
        };
        
        //특정 상전이 과정을 표현하는 enum상수를 반환
        public static Transition from(Phase src, Phase dst) {
            return TRANSITTIONS[src.ordinal()][dst.ordinal()];
        }
    }
}
 
//상전이 예제 클래스
enum PhaseEnum{ 
    SOLID, LIQUID, GAS;
    
    public enum Transition{
        MELT(SOLID,LIQUID),
        FREEZE(LIQUID,SOLID),
        BOIL(LIQUID,GAS),
        CONDENSE(GAS,LIQUID),
        SUBLIME(SOLID,GAS),
        DEPOSIT(GAS,SOLID);
        
        private final PhaseEnum src;
        private final PhaseEnum dst;
        
        Transition(PhaseEnum src, PhaseEnum dst){
            this.src = src;
            this.dst = dst;
        }
        
        //상전이 맵 초기화
        private static final Map<PhaseEnum, Map<PhaseEnum, Transition>> m =
             new EnumMap<PhaseEnum, Map<PhaseEnum, Transition>>(PhaseEnum.class);
        
        static {
            //세개의 빈 안쪽 맵을 값으로 사용하는 바깥 맵을 초기화
            for(PhaseEnum p : PhaseEnum.values()) {
                m.put(p, new EnumMap<PhaseEnum, Transition>(PhaseEnum.class));
            }
            
            //각각의 상전이 명칭 상수에 보관된 상전이 이전상태와
            //이후 상태정보를 사용해서 안쪽맵 초기화
            for(Transition trans : Transition.values()) {
                m.get(trans.src).put(trans.dst, trans);
            }
        }
        
        public static Transition from(PhaseEnum src, PhaseEnum dst) {
            return m.get(src).get(dst);
        }
    }
}
 

자료형

Map<PhaseEnum, Map<PhaseEnum, Transition>>

 

PLASMA의 새로운 상태를 추가하려고 할때

관계된 상전이 : 이온화(IONIZATION) : GAS → PLASMA,

역이온화(DEIONIZATION) : PLASMA → GAS

 

배열 예제에 추가하려 했을 때

1. Phase에 새로운 상수 추가

2. Phase.Transition에 새 상수 두개 추가

3. 원래 9개원소를 갖는 배열을 2차원 배열로 수정

 

Enum 예제에 추가할 때

1. Phase에 새로운 상수 추가

2. Phase.Transition에 새 상수 두개 추가

 

결론

· ordinal값을 배열 첨자로 사용하지말고 EnumMap을 사용하라.

 

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유