[JAVA] enum (열거형)

Coastby·2022년 12월 5일
0

JAVA

목록 보기
33/33

✅ 열거형 : 서로 관련된 상수를 편리하게 선언하기 위한 것

자바의 열거형은 '타입에 안전한 열거형 (typesafe enum)'이라 실제 값이 같아도 타입이 다르면 컴파일 에러가 발생한다.

열거형의 정의와 사용

○ 열거형의 정의

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

👉 예시

enum Direction {EAST, SOUTH, WEST, NORTH;}

○ 열거형의 사용

Direction dir = Direction.EAST;

열거형의 비교

열거형 상수간의 비교에는 == 를 사용할 수 있다. equals()보다 빠른 성능을 제공한다. 부등호는 사용 불가능하며 compareTo()를 사용해서 왼쪽이 크면 양수, 오른쪽이 크면 음수를 반환한다.

switch문에 사용

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

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

○ enum의 메서드

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을 사용할 때 얻을 수 있는 장점들은 아래와 같다.

  • 문자열과 비교해, IDE의 적극적인 지원을 받을 수 있습니다.
    • 자동완성, 오타검증, 텍스트 리팩토링 등등
  • 허용 가능한 값들을 제한할 수 있습니다.
  • 리팩토링시 변경 범위가 최소화 됩니다.
    • 내용의 추가가 필요하더라도, Enum 코드외에 수정할 필요가 없습니다.

이 외에도 얻은 수 있는 장점들이 있다.

1. 데이터들 간의 연관관계 표현

아래와 같이 if문을 이용하여 서로 다른 테이블에서 같은 의미이지만 다른 이름으로 불리는 변수들을 묶었었다. 이렇게되면 변수가 추가되면 코드량이 급격히 증가하게 되고, 중복되는 코드가 많다는 단점이 있다. 또한 한눈에 같은 의미라는 것을 알기가 어렵고 찾아봐야 하는 범위가 넓어진다.

enum으로 만들게되면 묶음을 한 눈에 확인할 수 있고, @Getter를 사용한다면 getter도 생략할 수 있어서 코드량이 확연히 줄어든다.

2. 상태와 행위를 한 곳에서 관리

enum의 필드로 메서드를 설정하면 변수에 따라 다른 계산식을 적용할 때 이를 강제할 수 있다. 코드에 따라 계산식을 설정하고, 이를 계산하려고 할 때 코드 조회 따로, 계산하는 클래스 따로 진행하게 된다.

이 때에 변수와 계산식과의 관계를 강제할 수 없다는 단점이 있다. 즉, long result에 들어가는 값이 어느 것이든 될 수 있어서 실수할 확률이 높다.
그리고

  • 똑같은 기능을 하는 메소드를 중복 생성할 수 있습니다.
    • 히스토리가 관리 안된 상태에서 신규화면이 추가되어야 할 경우 계산 메소드가 있다는 것을 몰라 다시 만드는 경우가 빈번합니다.
    • 만약 기존 화면의 계산 로직이 변경 될 경우, 신규 인력은 2개의 메소드의 로직을 다 변경해야하는지, 해당 화면만 변경해야하는지 알 수 없습니다.
    • 관리 포인트가 증가할 확률이 매우 높습니다.
  • 계산 메소드를 누락할 수 있습니다.
    • 결국 문자열과 메소드로 분리 되어 있기 때문에 이 계산 메소드를 써야함을 알 수 없어 새로운 기능 생성시 계산 메소드 호출이 누락될 수 있습니다.

이를 enum을 활용하면, 아래와 같이 선언할 수 있다.


👉 JPA annotation @Enumerated(EnumType.STRING)
JPA에서 Entity의 필드값에 위의 어노테이션을 붙여주면 Enum 필드가 테이블에 저장 시 숫자형인 1, 2, 3이 아닌, Enum의 name이 저장된다. EnumType.Ordinal(숫자형)을 사용하게 되면 상수값들 사이에 값이 추가될 경우 원래의 수가 다른 값을 가리키게되어 위험하게 된다.

3. 데이터 그룹 관리

결제 그룹을 관리할 때 어느 결제 종류에 속하는지를 확인하기 위해 아래와 같이 if문을 이용할 수 있다.

하지만 이는,

  • 둘의 관계를 파악하기가 어렵습니다.
    • 위 메소드는 포함관계를 나타내는 것일까요? 아니면 단순한 대체값을 리턴한것일까요?
    • 현재는 결제종류가 결제수단을 포함하고 있는 관계인데, 메소드만으로 표현이 불가능합니다.
  • 입력값과 결과값이 예측 불가능합니다.
    • 결제 수단의 범위를 지정할수 없어서 문자열이면 전부 파라미터로 전달 될 수 있습니다.
    • 마찬가지로 결과를 받는 쪽에서도 문자열을 받기 때문에 결제종류로 지정된 값만 받을 수 있도록 검증코드가 필요하게 됩니다.
  • 그룹별 기능을 추가하기가 어렵습니다.
    • 결제 종류에 따라 추가 기능이 필요할 경우 현재 상태라면 어떻게 구현 할수 있을까요?
    • 또다시 결제종류에 따른 if문으로 메소드를 실행하는 코드를 작성해야 할까요?

결제 종류에 따라 다른 메소드가 시행되는 코드를 작성할 때마다 결제종류를 분기하는 코드 (if문)를 작성해야 한다.

결제종류, 결제수단 등의 관계를 명확히 표현하며, 각 타입이 수행해야할 기능과 책임만 가질 수 있게 하기 위해 enum을 활용한다.

이제는 enum에게 직접 어떤 그룹인지 물어볼 수 있게되었다. (분류하는 코드를 반복해서 작성하지 않아도 된다)

또 생각할 문제점은 결제수단이 String으로 되어있다는 것이다. (List<String> payList) 이렇게되면 데이터에 저장하기에 불안정하며 관리하기 어렵다. 따라서 이 string도 enum으로 만든다.


profile
훈이야 화이팅

0개의 댓글