순간을 성실히, 화려함보단 꾸준함을

11주차 과제: Enum 본문

백기선님과 함께 하는 자바 스터디

11주차 과제: Enum

폭발토끼 2021. 7. 10. 12:53

목표

자바의 열거형에 대해 학습하세요.

학습할 것 (필수)

  1. enum 정의하는 방법
  2. enum이 제공하는 메소드
  3. java.lang.Enum
  4. EnumSet

1. enum 정의하는 방법

열거형이란?

- 서로 관련된 상수를 편리하게 선언하기 위한 것으로 여러 상수를 정의할 때 사용하면 유용하다. Java5에서 추가된 개념으로 열거형이 갖는 값뿐만 아니라 타입도 관리하기 때문에 보다 논리적인 오류를 줄일 수 있다.

정의

enum 열거형이름{ 상수명1, 상수명2, ...}

예를 들어 동,서,남,북 을 정의하는 열거형 Direction은 다음과 같다

enum Direction{ EAST,WEST,SOUTH,NORTH}

정의된 상수를 사용하는 방법은 열거형이름.상수명 이다.

class Unit{
    int x,y;        // 유닛의 위치
    Direction dir;    // 열거형을 인스턴스 변수로 선언

    void inti(){
        dir = Direction.EAST;    // 유닛의 방향을 EAST로 초기화
    }
}

열거형 상수간의 비교에서 ==을 사용할 수 있다. equals() 가 아닌 ==으로 비교가 가능하다는 것은 빠른 성능을 제공한다는 뜻
그러나 <,> 와 같은 비교연산자는 사용할 수 없고 compareTo()는 사용 가능하다.

    if(dir==Direction.EAST){
        x++;
    } else if(dir > Direction.WEST){    // 에러, 열거형 상수에 비교연산자 사용불가
        ...
    } else if(dir.compareTo(Direction.WEST > 0){    //compareTo() 사용가능
        ...
    }

CompareTo() 란?

compareTo() 함수는 두 Integer 객체에 저장된 int값(value)를 비교하여 같으면 0, 크면 -1, 작으면 1을 반환해주는 함수이다.
Comparable 인터페이스에 정의되어 있는 메소드이다.

switch문의 조건식에도 열거형을 사용할 수 있다.

    void move(){
        switch(dir){
            case EAST:    x++;
                break;
            case WEST:    x--;
                break;
            case SOUTH:    y++;
                break;
            case NORTH:    y--;
                break;
        }
    }

이때 주의할 점은 case문에 열거형의 이름은 적지 않고 상수의 이름만 적어야 한다.

열거형(enum)의 특징

  1. 열거형 상수들은 해당 열거형의 객체이다.

enum Direction { EAST, SOUTH, WEST, NORTH }

사실은 열거형 상수 하나하나가 Direction 객체이다. 위의 문장을 클래스로 표현해보자

    class Direction{
        static final Direction EAST = new Direction("EAST");
        static final Direction SOUTH = new Direction("SOUTH");
        static final Direction WEST = new Direction("WEST");
        static final Direction NORTH = new Direction("NORTH");

        private String name;

        private Direction(String name) { 
            this.name = name;
        }

Direction 클래스의 static 상수 EAST,SOUTH,WEST,NORTH 의 값은 객체의 주소이고, 이 값은 바뀌지 않으므로 ==으로 비교가 가능한 것이다.

  1. 열거형에 맴버를 추가할 수 있고, 생성자를 선언할 수 있다.

열거형에 맴버를 추가할 수 있는데, 이때 상수들을 선언하고 마지막에 ; 세미콜론을 반드시 추가하여야 한다.
또한, 생성자를 선언할 수 있다.

public class Main{
    public static void main(String[] args) {
        Direction dir = new Direction("North");        //Compile Error 발생
    }
}
enum Direction{
    EAST("E"), WEST("W"), SOUTH("S"), NORTH("N");

    String dir;
    Direction(String dir){
        this.dir=dir;
    }
    String getDir(){
        return this.dir;
    }
}

위의 소스는 컴파일에러가 발생한다. 그 이유는 열거형 생성자는 제어자가 묵시적으로 private이기 때문에 외부에서 열거형의 생성자를 호출 할 수 없다.

필요하다면 열거형 상수에 여러값을 지정할 수 있다.

enum Direction{
    EAST("E",1), WEST("W",2), SOUTH("S",3), NORTH("N",4);

    String dir;
    Direction(String dir,int number){    //private이 생략된 것 이다.
        this.dir=dir;
    }
    String getDir(){
        return this.dir;
    }
}

2. enum이 제공하는 메소드

  • T[] values() : enum의 요소들을 순서대로 enum 타입의 배열로 리턴한다.
  • T valueOf(Class enumType, String name) : 지정된 열거형에서 name 과 일치하는 열거형 상수를 반환한다.
  • Class getDeclaringClass() : 열거형의 Class객체를 반환한다.
  • String name() : 열거형 상수의 이름을 문자열로 반환한다.
  • int ordinal() : 열거형 상수가 정의된 순서를 반환한다.(0부터 시작)

T[] values() : enum의 요소들을 순서대로 enum 타입의 배열로 리턴한다.

public class Main{
    public static void main(String[] args) {
        for(Direction d : Direction.values())
            System.out.println(d);
    }
}
enum Direction{
    EAST, WEST, SOUTH, NORTH;
}
//출력
EAST
WEST
SOUTH
NORTH

T valueOf(Class enumType, String name) : 지정된 열거형에서 name 과 일치하는 열거형 상수를 반환한다.

public class Main{
    public static void main(String[] args) {
        //2가지의 방법이 존재한다.
        Direction dir1 = Enum.valueOf(Direction.class,"EAST");
        System.out.println(dir1);
        Direction dir2 = Direction.valueOf("EAST");
        System.out.println(dir2);
    }
}
enum Direction{
    EAST, WEST, SOUTH, NORTH;
}
//출력
EAST
EAST

int ordinal() : 열거형 상수가 정의된 순서를 반환한다.(0부터 시작)

public class Main{
    public static void main(String[] args) {
        int order;
        order = Direction.EAST.ordinal();
        System.out.println(order);
        order = Direction.WEST.ordinal();
        System.out.println(order);
        order = Direction.SOUTH.ordinal();
        System.out.println(order);
        order = Direction.NORTH.ordinal();
        System.out.println(order);
    }
}
enum Direction{
    EAST, WEST, SOUTH, NORTH;
}

3. java.lang.Enum

모든 Enum 클래스는 java.lang.Enum 클래스를 상속받는다.

또한, 자바는 다중 상속을 지원하지 않으므로 Enum 클래스는 다른 클래스를 상속받을 수 없다.

public class Main{
    public static void main(String[] args) {

    }
}
class Rabbit{
    int age;
    Rabbit(){}
}
enum Direction extends Rabbit{        //Compile Error 발생 (No extends clause allowed for enum)
    EAST, WEST, SOUTH, NORTH;
}

Enum타입은 고정된 상수들의 집합으로, 컴파일타임에 모든 값을 알고있어야 한다.(런타임이 아니다).
즉, 다른 클래스나 패키지에서 Enum 타입에 접근해서 동적으로 임의의 값을 정해줄 수 없다.

4. EnumSet

열거셋(EnumSet)은 Set 인터페이스를 기반으로 하면서 enum 열거요소들을 이용해서 보다 빠르고 강력하게 결과를 도출해 낼 수 있도록 자바 5 에서 등장한 개념이다.
EnumSet은 기술상으로 2^6(64)개를 넘지 않을 경우에 겉은 Set 기반이지만 내부적으로 long 데이터형의 비트 벡터를 사용하고 있다.

비트 벡터(Bitvector)

특별한 종류의 배열이다. 기본적으로 비트벡터는 저장 공간이 낭비되지 않도록 비트값(boolean 값)들을 배열에 꽉 채워넣기 위한 공간으로, 비트들을 최소의 크기로 꾸려 넣을 수 있게 한다.

enum Day{
    SUNDAY,MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY
}
public class Main{
    public static void main(String[] args) {
        EnumSet es = EnumSet.allOf(Day.class);        
        System.out.println(es);//출력 : [SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY]

        EnumSet es2 = EnumSet.copyOf(es);     
        System.out.println(es2);//출력 : [SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY]

        es2 = EnumSet.noneOf(Day.class);      
        System.out.println(es2); //출력 : []

        es = EnumSet.of(Day.FRIDAY,Day.WEDNESDAY);
        System.out.println(es); //출력 : [WEDNESDAY, FRIDAY]

        es2 = EnumSet.complementOf(es);
        System.out.println(es2);//출력 : [SUNDAY, MONDAY, TUESDAY, THURSDAY, SATURDAY]

        es2 = EnumSet.range(Day.TUESDAY,Day.FRIDAY);
        System.out.println(es2);//출력 : [TUESDAY, WEDNESDAY, THURSDAY, FRIDAY]
    }
}
  • copyOf(EnumSet s) : 매개변수로 들어온 EnumSet을 복사한다.
  • noneOf(Class elementType) : 비어있는 EnumSet을 반환한다.
  • of(E e1, E e2) : 열거형 상수 2개를 입력받아 새로운 EnumSet에 넣어 반환한다.
  • complementOf(EnumSet s) : 매개변수에 들어온 EnumSet의 열거형 상수들을 제외한 열거형 상수들을 새로운 EnumSet에 넣어 반환한다.
  • range(E from, E to) : 인자로 받은 열거형 상수 사이의 범위를 인덱스 순서대로 새로운 EnumSet에 넣어 반환한다. 단, 앞선 매개변수의 인덱스가 빠르면 런타임 에러가 발생한다.

reference

자바의 정석(남궁성)
[http://alecture.blogspot.com/2012/11/enumset.html]
[https://javabom.tistory.com/50]
[https://www.baeldung.com/java-enumset]