본문 바로가기

Java

java 열거형 enum 업무활용

Enum 이란?

Enum은 열거형 상수이다.
연관된 상수의 집합을 뜻한다.
기존에 final static int .. 등으로 사용하던 상수를 enum을 활용하여 사용할 수 있다.

장점.
일반 클래스의 경우 상수값 변경시 상수를 참조하는 소스를 재 컴파일 해야함.
열거형 상수는 참조하는 소스를 재 컴파일하지 않아도 된다.
열거형 상수간 비교는 == 를 사용할 수 있음. equals 가 아닌 == 로 비교가능한건 그만큼 빠르다는 것임.
하지만 비교연산자 >, < 는 사용이 불가능하고 compareTo는 사용이 가능하다.

컴파일 시점 체크를 통해 의도와는 다른 값이 변수에 할당되는것을 방지한다
Java의 enum은 실제 값이 같더라도 타입이 다르면 조건식의 결과가 false가 된다.

 

자주 사용하는 메소드
static E values() : 열거형의 모든 상수배열을 반환
static E valueOf(String name) : 전달된 문자열과 일치하는 열거형의 상수를 반환(참조를 얻을 수 있음)

protected void finalize() : Enum 클래스가 final 메소드를 가질 수 없게 됨
String name() : 해당 열거체의 상수 이름을 반환
int ordinal() : 해당 열거체의 상수가 정의된 순서를 반환(start:0)

 

실무에서 내가 활용해본 Enum에 대한 개인적인 생각
가끔 로직을 보면 if else 분기문 안의 조건식이 매우 형식적이고 일정한 패턴의 가지는 경우가 있다.
여러차례 동일한 패턴으로 분기가 되는 경우 분기문의 조건이 하나씩 늘때마다 분기조건을 추가해야하고,
동일한 패턴이 쭉 길게 늘여지는 형태라서 가독성이 떨어질 뿐더러, 한눈에 보기에도 고민없이 대충만든 소스처럼 보인다.
이럴때 Enum을 활용하면 업무로직이 늘어날 때마다 추가하던 형식적인 if else 문을 수정하지 않아도 되고,
열거형 상수로 정의하여 사용시 가독성도 높아지는 결과를 느꼈다.

 

Enum 기본사용 예제

public enum CoffeeMenu {
 
    AMERICANO, LATTE, HOTCHOCO;
 
    CoffeeMenu() {
        System.out.println("Constructor this " + this);
    }
 
    public static void main(String[] args) {
        /*
            출력결과
            Constructor this AMERICANO
            Constructor this LATTE
            Constructor this HOTCHOCO
            
            생성자 함수에서 AMERICANO, LATTE, HOTCHOCO이 모두 출력되었다.
            이는 enum Type 상수 하나하나 모두 CoffeeMenu의 객체이고 각각 생성자 호출을 통해 객체가 생성됨을 알수있다.
        */
    }
}
 
cs

 

enum역시 class이고 class로 표현시 아래와 같음.

class CoffeeMenu {
 
    static final CoffeeMenu AMERICANO = new CoffeeMenu("AMERICANO");
    static final CoffeeMenu LATTE = new CoffeeMenu("LATTE");
    static final CoffeeMenu HOTCHOCO = new CoffeeMenu("HOTCHOCO");
 
    private String menu;
 
    private CoffeeMenu1(String menu)  {
      this.menu = menu;
    }
}
 
cs

CoffeeMenu class의 static 상수 AMERICANO, LATTE, HOTCHOCO의 값은 객체의 주소이다. 

이 값들은 바뀌지 않기 때문에 == 로도 비교가 가능하다.

 

 

열거형 상수에 해당하는 특정값이 필요한 경우 아래와 같이 사용가능

public enum CoffeeMenu2 {
 
    AMERICANO(2000), LATTE(3000), HOTCHOCO(4000"america");
 
    private int price;
    private String areaOfProduction;
 
    CoffeeMenu2(int price) {
        this.price = price;
        System.out.println("Constructor this " + this + ", price " + price);
    }
 
    CoffeeMenu2(int price, String areaOfProduction) {
        this.price = price;
        this.areaOfProduction = areaOfProduction;
        System.out.println("Constructor this " + this + ", price " + price + ", areaOfProduction " + areaOfProduction);
    }
 
    public int getAmount() {
        return this.price;
    }
 
    public String getAreaOfProduction() {
        return this.areaOfProduction;
    }
 
    public static void main(String[] args) {
        // 위의 예시처럼 enum 역시 class 이므로 생성자 오버로드가 가능하다.
        // 출력시
        //Constructor this AMERICANO, price 2000
        //Constructor this LATTE, price 3000
        //Constructor this HOTCHOCO, price 4000, areaOfProduction america
    }
}
cs

 

if else 조건문으로 분기한 내용을 enum을 활용해서 변경해보도록 하자.
아래는 if else 문을 사용한 소스이다.

public class CoffeeMenuIfElse {
 
    int amount(String menuName, int quantity) {
 
        int amount = 0;
        if ("AMERICANO".equals(menuName)) {
            amount = 2000 * quantity;
        } else if("LATTE".equals(menuName)) {
            amount = 3000 * quantity;
        } else if("HOTCHOCO".equals(menuName)) {
            amount = (4000 / 2* quantity;
        }
        return amount;
    }
 
    public static void main(String[] args) {
 
        CoffeeMenuIfElse coffee = new CoffeeMenuIfElse();
        int americanoAmount = coffee.amount("AMERICANO"5); // 10000
        int hotChocoAmount = coffee.amount("HOTCHOCO"5); // 10000
    }
}
cs

 

enum 사용하여 변경하였다.
추상메서드를 정의하여 각각 구현함.

public enum CoffeeMenuV1 {
 
    AMERICANO(2000) {
        int amount(int quantity) {
            return price * quantity;
        }
    }, LATTE(3000) {
        int amount(int quantity) {
            return price * quantity;
        }
    }, HOTCHOCO(4000) {
        int amount(int quantity) {
            // discount
            return (price / 2* quantity;
        }
    };
 
    CoffeeMenuV1(int price) {
        this.price = price;
    }
 
    protected int price;
 
 
    // 추상 메서드 사용
    abstract int amount(int quantity); // 추상 메서드 선언시 열거형 상수가 추가한 추상 메서드를 반드시 구현해야 함.
}
cs

 

enum으로 변경하게되면 Enum.valueOf(menuName)를 사용하여 if else로 분기하는 로직을 대체할 수 있다.


이번에는 enum과 lamda를 사용하여 다시 변경하였다.
java8 이상 사용가능.

public enum CoffeeMenuV2 {
 
    AMERICANO(value -> 2000 * value),
    LATTE(value -> 3000 * value),
    HOTCHOCO(value -> (4000 / 2* value);
 
    private Function<Integer, Integer> expression;
 
    CoffeeMenuV2(Function<Integer, Integer> expression) {
        this.expression = expression;
    }
 
    public int getTotalAmount(int price) {
        return expression.apply(price);
    }
}
cs

 

로직을 class로 분리하여 사용하도록 변경도 가능.
필자는 실제 업무에서 분기후 처리할 로직이 많아서 따로 객체를 생성하여 처리하였다.

단점은 아래의 소스를 수정하고나서 클래스를 Control + 클릭하면(new Americano()) 이클립스가 뻗는 현상이 자주 발생되었다.
enum에 lamda를 사용하여 객체생성을 해서 그런지 메모리를 많이 잡아먹나보다.
new Class 를 마우스로 클릭하지 않으면 그런현상 발생되지 않아 그나마 다행이었다.

public enum CoffeeMenuV3 {
 
    // 로직이 많은 경우는 객체를 따로 생성하여 관리하는것도 가능함.
    AMERICANO(() -> new Americano()),
    LATTE(() -> new Latte()),
    HOTCHOCO(() -> new HotChoco());
 
    private Supplier<Menu> expression;
    CoffeeMenuV3(Supplier<Menu> expression) {
        this.expression = expression;
    }
 
    public Menu create() {
        return expression.get();
    }
}
 
public class Americano implements Menu {
 
    @Override
    public int amount(int quantity) {
        return 2000 * quantity;
    }
}
 
public class HotChoco implements Menu {
 
    @Override
    public int amount(int quantity) {
        return (4000 / 2* quantity;
    }
}
 
public class Latte implements Menu {
 
    @Override
    public int amount(int quantity) {
        return 3000 * quantity;
    }
}
 
public interface Menu {
 
    public int amount(int quantity);
}
 
public class CoffeeMenuMain {
 
    public static void main(String[] args) {
 
        /* Old Version */
 
        /* Version 1 */
        String inputValue = "AMERICANO";
        int inputQuantity = 5;
        //System.out.println("americano 5 total amount : " + CoffeeMenuV1.valueOf(inputValue).amount(inputQuantity)); // 10000
 
        /* Version 2 */
        //System.out.println("americano 5 total amount : " + CoffeeMenuV2.valueOf(inputValue).getTotalAmount(5)); // 10000
 
        /* Version 3 */
        Menu menu = CoffeeMenuV3.valueOf(inputValue).create();
        //System.out.println("americano 5 total amount : " + menu.amount(5)); // 10000
 
        System.out.println(CoffeeMenu2.valueOf("HOTCHOCO").getAreaOfProduction());
    }
}
cs

간략하게나마 Enum을 공부하면서 인터넷을 참고하여 테스트 해보았던 소스를 정리해보았다.
너무 허접하게 정리되었으나 글을 많이 쓰다보면 조금더 알아보기 쉽게 정리될거라 빋는다 ㅠㅠ
이것으로 Enum 활용을 마무리 하겠다.