✅ 열거형 : 서로 관련된 상수를 편리하게 선언하기 위한 것
자바의 열거형은 '타입에 안전한 열거형 (typesafe enum)'이라 실제 값이 같아도 타입이 다르면 컴파일 에러가 발생한다.
enum 열거형이름 {상수명1, 상수명2, ...}
👉 예시
enum Direction {EAST, SOUTH, WEST, NORTH;}
Direction dir = Direction.EAST;
열거형 상수간의 비교에는 ==
를 사용할 수 있다. equals()보다 빠른 성능을 제공한다. 부등호는 사용 불가능하며 compareTo()
를 사용해서 왼쪽이 크면 양수, 오른쪽이 크면 음수를 반환한다.
case문에 열거형의 이름은 적지 ㅇ낳고 상수의 이름만 적어야 한다.
void move(){
switch (dir){
case EAST: x++;
break;
case WEST: x--;
break;
case SOUTH: y++;
break;
case NORTH: y--;
break;
}
}
dir = Direction.valueOf("EAST");
System.out.println(dir.name()); //EAST
System.out.println(dir.ordinal()); //0
Direction[] dArr = Direction.values(); //[Direction.EAST, Direction.SOUTH,...]
열거형 상수에 값을 지정하기 위해서는 상수 이름 옆에 원하는 값을 ()
안에 적어준다.
enum Direction {EAST(1), SOUTH(5), WEST(-1), NORTH(10);}
그리고 지정된 값을 저장할 수 있는 인스턴스 변수와 생성자를 추가한다.
먼저 열거형 상수를 모두 정의한 다음에 다른 멤버들을 추가해야한다. 그리고 열거형 상수의 마지막에 ;
도 있어야한다.
enum Direction {
EAST(1), SOUTH(5), WEST(-1), NORTH(10);
//필드 추가 (인스턴스 변수), 꼭 final이어야하는 건 아니다
private final int value;
//생성자 추가 : 기본적으로 private
Direction(int value){
this.value = value;
}
//getter
public int getValue() {
return value;
}
}
열거형 Direction에 새로운 생성자가 추가되었지만, 열거형의 객체는 생성할 수 없다. 열거형의 생성자는 제어자가 묵시적으로 private이기 때문이다.
Direction d = new Direction(1); //에러. 열거형의 생성자는 외부에서 호출불가
필요하다면 열거형 상수에 여러 값을 지정할 수 있다. 다만 그에 맞는 인스턴스 변수와 생성자 등을 새로 추가해야 한다.
열거형에 추상 메서드를 선언하면, 각 열거형 상수가 이 추상 메서드를 반드시 구현해야 한다.
public enum Direction {
EAST(1) {int fare(int distance) {return distance*BASIC_FARE;}},
...
abstract int fare(int distance); //추상 메서드 선언
...
}
int fare = Direction.EAST.fare(100);
열거형은 상수 하나하나가 객체이다.
Direction 클래스의 static 상수 EAST, SOUTH, WEST, NORTH의 값은 객체의 주소이고, 이 값은 바뀌지 않는 값이므로 ==
로 비교가 가능한 것이다.
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;
}
}
참고: https://effortguy.tistory.com/24
참고 : https://techblog.woowahan.com/2527/
기본적으로 enum을 사용할 때 얻을 수 있는 장점들은 아래와 같다.
이 외에도 얻은 수 있는 장점들이 있다.
아래와 같이 if문을 이용하여 서로 다른 테이블에서 같은 의미이지만 다른 이름으로 불리는 변수들을 묶었었다. 이렇게되면 변수가 추가되면 코드량이 급격히 증가하게 되고, 중복되는 코드가 많다는 단점이 있다. 또한 한눈에 같은 의미라는 것을 알기가 어렵고 찾아봐야 하는 범위가 넓어진다.
enum으로 만들게되면 묶음을 한 눈에 확인할 수 있고, @Getter
를 사용한다면 getter도 생략할 수 있어서 코드량이 확연히 줄어든다.
enum의 필드로 메서드를 설정하면 변수에 따라 다른 계산식을 적용할 때 이를 강제할 수 있다. 코드에 따라 계산식을 설정하고, 이를 계산하려고 할 때 코드 조회 따로, 계산하는 클래스 따로 진행하게 된다.
이 때에 변수와 계산식과의 관계를 강제할 수 없다는 단점이 있다. 즉, long result
에 들어가는 값이 어느 것이든 될 수 있어서 실수할 확률이 높다.
그리고
이를 enum을 활용하면, 아래와 같이 선언할 수 있다.
👉 JPA annotation
@Enumerated(EnumType.STRING)
JPA에서 Entity의 필드값에 위의 어노테이션을 붙여주면 Enum 필드가 테이블에 저장 시 숫자형인 1, 2, 3이 아닌, Enum의 name이 저장된다.EnumType.Ordinal
(숫자형)을 사용하게 되면 상수값들 사이에 값이 추가될 경우 원래의 수가 다른 값을 가리키게되어 위험하게 된다.
결제 그룹을 관리할 때 어느 결제 종류에 속하는지를 확인하기 위해 아래와 같이 if문을 이용할 수 있다.
하지만 이는,
결제 종류에 따라 다른 메소드가 시행되는 코드를 작성할 때마다 결제종류를 분기하는 코드 (if문)를 작성해야 한다.
결제종류, 결제수단 등의 관계를 명확히 표현하며, 각 타입이 수행해야할 기능과 책임만 가질 수 있게 하기 위해 enum을 활용한다.
이제는 enum에게 직접 어떤 그룹인지 물어볼 수 있게되었다. (분류하는 코드를 반복해서 작성하지 않아도 된다)
또 생각할 문제점은 결제수단이 String으로 되어있다는 것이다. (List<String> payList
) 이렇게되면 데이터에 저장하기에 불안정하며 관리하기 어렵다. 따라서 이 string도 enum으로 만든다.