열거형 (Enums) 이란?

열거형이란 서로 관련된 상수를 편리하게 선언하기 위한 것으로 여러 상수를 정의할 때 사용하면 유용하다.

JDK1.5부터 새로 추가되었다.

 

열거형의 정의와 사용

enum Direction{
    EAST, WEST, SOUTH, NORTH
}

다음과 같이 괄호{} 안에 상수의 이름을 나열하기만 하면 된다.

class Unit{
    Direction dir;
    
    public Unit(){
        this.dir = Direction.EAST;
    }
}

사용하는 방법은 열거형이름.상수명 으로 static변수를 참조하는 것과 동일하다.

 

열거형 상수의 비교

if (dir == Direction.WEST) {...} //가능
else if(dir > Direction.SOUTH ) {...} // 불가능
else if(dir.compareTo(Direction.NORTH) {...} //가능

또한 같은 클래스 내의 열거형 상수간의 비교에는 ‘==’를 사용할 수 있다.

equals()가 아닌 ==를 사용하면 더 빠른 성능을 제공한다.

 

그러나 > , ≥ , <, ≤ 와 같은 비교연산자는 사용할 수 없으며 compareTo() 메서드는 사용가능하다.

 

열거형과 switch문

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

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

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

 

모든 열거형의 조상 - java.lang.Enum

Class<E> getDeclaringClass() //열거형의 Class 객체 반환
String name() //열거형 상수의 이름을 문자열로 반환
int ordinal() //열거형 상수가 정의된 순서 반환 (0부터 시작)
T valueOf(Class<T> enumType, String name) //name 문자열과 과 일치하는 열거형 상수 반환

static E values() // 열거형의 모든 상수를 배열에 담아 반환한다.
static E valueOf(String name) //name 문자열과 과 일치하는 열거형 상수 반환

Enum 클래스에는 위와 같은 메서드들이 정의되어 있다.

 

열거형에 멤버 추가하기

Enum클래스에 정의된 ordinal()이 열거형 상수가 정의된 순서를 반환하지만,

이 값을 열거형 상수의 값을 사용하지 않는 것이 좋다. 이 값은 내부적인 용도로만 사용되기 위한 값이다!

 

열거형 상수 값이 불연속적인 경우는 다음과 같이 추가해줄 수 있다.

  • 위와 같이 상수 이름 옆에 원하는 값을 괄호 ()로 붙여준다
  • 지정된 값을 저장하는 인스턴스 변수와 생성자를 추가한다.
enum Direction{
    EAST(-1), WEST(1), SOUTH(-2), NORTH(2);
    
    private final int value;
    
    Direction(int value){ 
        this.value = value 
    }
    
    public int getValue() {
        return this.value;
    }
}

열거형에 생성자가 추가되었지만 다른 코드에서 열거형을 새로 생성할 수는 없다.

이는 열거형의 생성자가 묵시적으로 private이기 때문이다.

필요하다면 하나의 열거형 상수에 여러 값을 지정하는 것도 가능하다.

 

열거형에 추상 메서드 추가하기

enum Client {
    BASIC(0.1) {
        double getDiscountFee(int amount) {return amount * DISCOUNT_RATE;} 
    }, 
    PRIMIUM(0.2) {
        double getDiscountFee(int amount) {return amount * DISCOUNT_RATE;}
    }, 
    VIP(0.3) {
        double getDiscountFee(int amount) {return (amount - 1000) * DISCOUNT_RATE;}
    };
    
    protected final double DISCOUNT_RATE;
    
    Client(double discountRate){
        this.DISCOUNT_RATE = discountRate;
    }
    
    abstract double getDiscountFee(int amount);
}

System.out.println(Client.BASIC.getDiscountFee(10000)); //1000.0
System.out.println(Client.PRIMIUM.getDiscountFee(10000)); //2000.0
System.out.println(Client.VIP.getDiscountFee(10000)); //2700.0

다음과 같이 enum에 추상 메서드를 선언하고 각 상수마다 구현을 다르게 적용할 수 있다.

그런데 많이 쓰일 것 같진 않으므로 참고만 하자.

 

열거형의 이해

enum Direction{
    EAST, WEST, SOUTH, NORTH
}

우리가 enum Direction을 생성하면 사실 컴파일러가 Enum<Direction>을 상속받는 Deirction 클래스를 생성해준다.

public final class **Direction** extends **Enum<Direction>** {
  public final Direction EAST;
  public final Direction WEST;
  ...
  ...

  static {
    EAST = new Direction ("EAST", 0);
    WEST = new Direction ("WEST", 0);
    ...
    ...
  }
}

그 클래스 안에는 static Direction 인스턴스 변수로 우리가 설정한 WEST, EAST 등의 값이 들어가며

클래스가 로딩될때 static{} 구문을 통해 초기화된다.

 

이러한 구조 덕분에 한번 생성된 enum 상수는 힙 영역에서 공통으로 관리되어 == 연산이 가능하다.

 

또한 Enum<Direction>을 상속받는 Direction은 Enum의 compareTo() 메서드를 상속받는데

Enum의 compareTo() 메서드는 Direction 객체만을 인자로 받을 수 있다.

public abstract interface Enum<E extends Enum<E>> implements ...{
...
  public final int compareTo(E o) {
    ...
    return this.ordinal - o.ordinal;
  }
}

이러한 구조를 Enum 클래스를 Enum<E extends Enum<E>>로 선언해버림으로서 해결했다!

 

이게 도대체 뭔 구조이냐 싶겠지만

E 대신에 Direction을 넣어서 다시 생각해보면

Enum<E extends Enum<Direction>>

 

그니까 컴파일러가 만든 Enum<Direction>을 상속받는 Direction을 받아온 후

compareTo의 타입 확인에 사용하겠다 라는 이야기로 보인다.

 

제대로 이해하기도 복잡하다.

 

EnumSet

java.util 패키지에서 제공하는 Enum과 함께 작동하는 컬렉션이다.

  • enum 값만 저장할 수 있고, 모든 값은 같은 enum 에 속해야한다.
  • null값을 추가하는 것을 허용하지 않는다. (NullPointerException을 발생시킨다)
  • 모든 메서드가 산술 비트 연산자로 구현되어 있다.
  • HashSet보다 적은 메모리를 차지하며 훨씬 빠른 성능을 낸다
Set<Direction> es = EnumSet.allOf(Direction.class);
EnumSet.noneOf(Direction.class);

Set<Direction> es = EnumSet.of(Direction.EAST, Direction.WEST);

다음과 같이 생성하며 사용방법은 Set 인터페이스를 구현했으므로 Set의 사용법과 같다.

+ Recent posts