C에서는 enum이 바로 숫자이기 때문에 배열의 인덱스로 자주 활용이 된다. 하지만 JAVA에서는 이것이 명시적이지 않아서 잘 모르는 경우가 많이 있다. 여기서는 JAVA에서 enum 타입을 배열의 인덱스로 활용하는 방법과 더불어 C언어의 enum보다 나은 점도 함께 알아보도록 하겠다.


해결하고자 하는 문제

다음과 같은 클래스가 있다고 하자. 대략 동서남북으로의 거리를 나타내는 클래스라고 생각하면 되겠다.

class Distance{

    private int east;

    private int west;

    private int north;

    private int south;

    public void setEast(int east){ this.east = east; }

    public void setWest(int west){ this.west = west; }

    public void setNorth(int north){ this.north = north; }

    public void setSouth(int south){ this.south = south; }

    public int getEast() { return east; }

    public int getWest() { return west; }

    public int getNorth() { return north; }

    public int getSouth() { return south; }

}

클래스 선언 상으로 보면 큰 문제는 없어보이지만 다만 마음에 걸리는 것은 중복이 많다는 점이다.

그래서 잘 들여다 보면 모두 같은 타입(int)의 변수를 선언하고 있고, 모두 getter와 setter를 제공하고 있음을 알 수 있다. 그리고 동서남북을 나타내는 변수들이므로 서로 연관성도 있어 보인다. 이런 경우에는 enum 타입을 하나 선언해서 동서남북을 가리키는 서브 타입을 만들고 이것을 인덱스로 활용하는 방법을 사용하면 중복이 제거된다.

다음과 같은 과정을 통해서 클래스를 수정해 볼 수 있다. 우선 방향을 나타내는 enum을 선언한다. Direction이라는 클래스명을 사용하도록 하겠다.

방향을 나타내는 enum Direction

enum Direction{

    EAST,

    WEST,

    NORTH,

    SOUTH;

}

이제 enum을 선언했으니 Distance 클래스에서 이를 이용하면서 코드를 간결하게 바꿔 보도록 하자. 바꾸는 방법은 우선 각 방향별로 선언되었던 변수를 하나의 배열로 선언하도록 한다. 방향이 4개니까 4개짜리 배열이 되겠다. 그런 다음 getter와 setter가 Direction을 매개 변수로 받도록 수정한다. 이제 중요한 부분이 남았다. enum 타입인 Direction  하위 타입을 어떻게 인덱스 숫자로 바꾸느냐는 것인데, JAVA에서는 ordinal() 이라는 함수를 통해 숫자로 바꿀 수 있다. int ordinal() 함수는 해당 서브 enum 타입의 정의 순서에 따른 숫자를 리턴한다. 가령 EAST.ordinal() = 0, WEST.ordinal() = 1......와 같은 방식이다. 이것은 배열의 인덱스 값과 같은 값이다. 따라서 전혀 문제 없이 사용할 수 있다.

class Distance 수정

class Distance{

    private int distance[] = new int[4];

    public int get(Direction direction){

        return distance[direction.ordinal()]; 

    }

    public void set(Direction direction, int value){ 

        distance[direction.ordinal()] = value; 

    }

}

이것으로 일단 원하는 대로 수정을 완료 하긴 하였다. 하지만 조금만 더 깊이 들어가 보자. 우리는 이미 동서남북이 4방향이라는 것을 알고 있고, 이에 따라 distance 변수의 배열 길이를 4로 지정했다. 하지만 만약 새로운 방향이 추가 된다면 어떻게 될 것인가? 물론 사용시에 예외가 발생하게 되겠지만 보다 근본적으로 이 문제를 해결할 방법이 있다. JAVA의 enum 타입은 기본적으로 static final이다. 따라서 컴파일 타임에 이미 enum 하위 타입의 개수도 정해진다. enum 하위 타입의 개수를 알아내는 방법은 보통 Type.values().length 를 이용한다. .values() 함수는 enum 하위 타입들을 배열화하여 리턴해주는 함수이다. .length는 잘 알다시피 배열의 길이를 나타내는 배열 객체의 변수이다. 그리고 이들은 앞서 말한 바와 같이 컴파일 타임에 결정되기 때문에 배열의 개수 초기화에도 사용할 수 있다.


values().length 를 이용한 배열 개수 초기화

class Distance{

    private int distance[] = new int[Direction.values().length]; // 이부분이 수정되었다.

    public int get(Direction direction){ 

        return distance[direction.ordinal()]; 

    }

    public void set(Direction direction, int value){ 

        distance[direction.ordinal()] = value; 

    }

}

이렇게 하면 Direction에 NORTH_EAST, SOUTH_EAST 등 새로운 하위 타입이 추가될 때마다 Direction.values().length 값도 함께 바뀌게 되고, 따라서 방향의 추가/삭제 시에도 배열의 길이에 의한 문제점이 발생하지 않게 된다.


완성된 코드를 모두 합쳐 보면 다음과 같다.


최종 결과물

enum Direction{

    EAST,

    WEST,

    NORTH,

    SOUTH;

}

class Distance{

    private int distance[] = new int[Direction.values().length];

    public int get(Direction direction){ 

        return distance[direction.ordinal()]; 

    }

    public void set(Direction direction, int value){ 

        distance[direction.ordinal()] = value; 

    }

}


enum Direction과 Distance 클래스를 혼합하여 사용하는 방법은 아래와 같다.


main() 함수

public static void main(String[] args) {

    Distance distance = new Distance();

    distance.set(Direction.EAST, 100);

    distance.set(Direction.WEST, 200);

    distance.set(Direction.NORTH, 300);

    distance.set(Direction.SOUTH, 400);

    System.out.println(Direction.EAST + " " + distance.get(Direction.EAST));

    System.out.println(Direction.WEST + " " + distance.get(Direction.WEST));

    System.out.println(Direction.NORTH + " " + distance.get(Direction.NORTH));

    System.out.println(Direction.SOUTH + " " + distance.get(Direction.SOUTH));

}

출력되는 결과물은 다음과 같다.


출력 결과

EAST 100

WEST 200

NORTH 300

SOUTH 400


원하던 값이 잘 출력됨을 알 수 있다.


이 과정이 시사하는 바가 있는데 이것을 한번 정리해 보면 다음과 같다.

1. 유사한 코드가 반복적으로 나타나는 경우에는 반복을 줄일 수 있는 방법을 생각해 봐야 한다.

2. 유사성을 표현할 수 있는 enum이나 상위 클래스를 선언할 수 있는지를 살펴 보아야 한다.

3. 변수들이 반복적으로 선언된 경우라면 enum과 배열을, if문이나 switch 문이 있을 경우에는 클래스를 이용한다.(클래스를 이용하는 방법에는 상태/전략 패턴이 있다.)

4. 배열을 이용할 경우 추가적인 타입 선언에 대해서도 고려 해야 한다.

Posted by 이세영2
,