김코딩

이놈의 Enum, 그거 어떻게 쓰는건데? 본문

TIL

이놈의 Enum, 그거 어떻게 쓰는건데?

김코딩딩 2025. 4. 22. 11:09

필자는 요즘 계산기 프로그램을 구현하는 과제를 수행 중입니다.
그 과정에서 사칙연산 기호를 관리하기 위해 enum을 활용하라는 요구사항이 있었는데요. 이번 글에서는 도대체 그 ENUM이라는 녀석이 무엇인지, 그리고 왜, 어떻게 써야 하는지 차근차근 알아보겠습니다.


Enum이란? – 이놈의 정체부터 알아보자

컴퓨터 프로그래밍에서 열거형(enumerated type, enumeration), 이넘(enum), 팩터(factor ← R 프로그래밍 언어와 통계학의 범주형 변수에서 부르는 명칭)는 요소, 멤버라 불리는 명명된 의 집합을 이루는 자료형이다. 열거자 이름들은 일반적으로 해당 언어의 상수 역할을 하는 식별자이다. - wikipedia

Enum 정의하는 방법

상수의 이름을 나열하면 됩니다.

enum Operator {
    ADD,       // 덧셈
    MINUS,     // 뺄셈
    MULTIPLY,  // 곱셈
    DIVIDE     // 나눗셈
}

 

enum에서 정의된 상수를 사용하는 방법은 클래스의 static 변수를 참조하는 것과 같이 열거형이름.상수명 입니다.

(예시: Operator.ADD ,  Operator.MINUS, Operator.MULTIPLY, Operator.DIVIDE)

 

열거형 상수에는 필드를 가질 수 있습니다. 추가하고 싶은 필드를 정의하고 생성자의 인자로 추가 한 뒤 각각의 상수에 값을 입력하면 됩니다. 단, 열거형 상수에 값을 부여할 경우 이에 해당하는 생성자도 함께 정의해주어야 합니다.

enum OperatorType {
    ADD('+'),
    MINUS('-'),
    MULTIPLY('*'),
    DIVIDE('/');
    
    private final char symbol;

    // enum도 생성자를 가질 수 있어요! 단, 항상 private
    OperatorType(char symbol) {
        this.symbol = symbol;
    }

    public char getSymbol() {
        return symbol;
    }
}

 

enum에서 생성자를 정의한다면 private으로 선언해야 합니다. 다른 접근제어자로 선언 시 컴파일 에러가 발생합니다.


Enum? – 왜 사용하는건데?

우선 예시를 하나 보여드리겠습니다.

Enum을 사용하지 않았을 때 (문자열 비교)

아래는 주문 상태를 나타낼 때 문자열을 직접 비교해서 처리하는 코드입니다.

public class OrderService {
    public void processOrder(String status) {
        if (status.equals("PENDING")) {
            System.out.println("주문 대기 중입니다.");
        } else if (status.equals("SHIPPED")) {
            System.out.println("배송 중입니다.");
        } else if (status.equals("DELIVERED")) {
            System.out.println("배송 완료!");
        } else {
            System.out.println("알 수 없는 상태입니다.");
        }
    }
}
  • 오타가 나도 컴파일은 되지만, 실행 중 오류가 발생합니다.(런타임 오류)
  • IDE의 자동 완성 기능을 쓸 수 없습니다.
  • 상태가 추가되면 모든 코드에 하나하나 반영해줘야 합니다.

Enum을 사용했을 때

public enum OrderStatus {
    PENDING,
    SHIPPED,
    DELIVERED
}
public class OrderService2 {
    public void processOrder(OrderStatus status) {
        switch (status) {
            case PENDING -> System.out.println("주문 대기 중입니다.");
            case SHIPPED -> System.out.println("배송 중입니다.");
            case DELIVERED -> System.out.println("배송 완료!");
        }
    }
}

  • 오타 방지: 사진에서 보이는 것 처럼 오타가 나면 IDE가 오류를 감지하여 오타를 방지할 수 있다.
  • 유지보수 용이: 값 추가/변경 시 한 곳만 수정하면 된다.
  • 가독성 향상: 의미 있는 이름으로 코드가 명확해진다.

Enum - 단지 값만 정리해주나?

사실 Enum에도 행동을 부여할 수 있습니다. Java에서는 람다식을 활용하여 각 enum 상수마다 다른 행동을 지정할 수 있게 해줍니다.

 

① 사칙연산을 if-else로만 처리할 때

public double calculate(double a, double b, char op) {
    if (op == '+') return a + b;
    else if (op == '-') return a - b;
    else if (op == '*') return a * b;
    else if (op == '/') return a / b;
    else throw new IllegalArgumentException("잘못된 연산자입니다.");
}
  • 연산자가 많아지면 if가 점점 길어진다.
  • 연산자를 실수로 하나 빼먹으면 버그가 발생한다.
  • 연산자 관련 코드가 흩어져있다.

② Enum으로 연산자 분리

public enum Operator {
    ADD('+'), SUB('-'), MUL('*'), DIV('/');

    private final char symbol;
    Operator(char symbol) {
        this.symbol = symbol;
    }
}
  • 연산자 기호를 Enum으로 모아서 관리가 편하게 되었다.
  • 하지만 아직도 계산은 외부에서 if문이나 switch문을 활용하여 처리해야한다.

③ Enum 안에 계산 기능 넣기

enum Operator {
    ADD('+') {
        public double apply(double a, double b) { return a + b; }
    },
    MINUS('-') {
        public double apply(double a, double b) { return a - b; }
    },
    MULTIPLY('*') {
        public double apply(double a, double b) { return a * b; }
    },
    DIVIDE('/') {
        public double apply(double a, double b) {
            if (b == 0){
                throw new ArithmeticException("분모는 0이 될 수 없습니다.");
            } else{
                return a / b;
            }
    }};
}

 

 

  • 각 연산자에 고유 행동을 부여해줍니다.
  • enum을 활용하여 연산 기능도 사용할 수 있습니다.
  • 하지만 코드가 길고 중복이 많습니다.

 

④ Enum에 람다 사용

import java.util.function.DoubleBinaryOperator;

public enum Operator {
    ADD('+', (a, b) -> a + b),
    MINNUS('-', (a, b) -> a - b),
    MULTIPLY('*', (a, b) -> a * b),
    DIVIDE('/', (a, b) -> {
        if (b == 0) throw new ArithmeticException("0으로 나눌 수 없습니다.");
        return a / b;
    });

    private final char symbol;
    private final DoubleBinaryOperator operation;

    Operator(char symbol, DoubleBinaryOperator operation) {
        this.symbol = symbol;
        this.operation = operation;
    }

    public double apply(double a, double b) {
        return operation.applyAsDouble(a, b);
    }
}

 

DoubleBinaryOperator 함수형 인터페이스를 활용하여 람다식을 사용할 수 있습니다.

 

DoubleBinaryOperator란?

java.util.function 패키지에 있는 함수형 인터페이스입니다.
(매개변수 2개 → double, 반환도 double)
@FunctionalInterface
public interface DoubleBinaryOperator {
    double applyAsDouble(double a, double b);
}

Enum이 제공하는 메서드

메서드 이름 설명
toString() ENUM 상수의 이름을 문자열로 반환한다. name() 메서드와 유사하지만, toString() 은 직접 오버라이드 할 수 있다.
name() ENUM 상수의 이름을 문자열로 반환한다.
ordinal() ENUM 상수의 선언 순서(0부터 시작)를 반환한다.
valueOf() 인자로 받은 이름과 같은 Enum 값으로 반환한다.
values() 선언된 모든 Enum 값을 순서대로 배열에 담아서 반환한다.

 

 


정리 – Enum, 이놈 이제 좀 알겠니?

 

  • enum은 관련된 상수들을 하나의 타입으로 묶어서 표현할 수 있는 유용한 도구입니다.
  • 단순히 값만 저장하는 것이 아니라, 행동(메서드)까지 정의할 수 있어 데이터와 로직을 함께 묶을 수 있는 객체지향적인 코드를 작성할 수 있습니다.
  • 문자열 비교처럼 오타 위험 없이, IDE의 자동완성도 지원받을 수 있어서 실수를 줄일 수 있습니다.
  • 람다식, DoubleBinaryOperator와 함께 사용하면 간결하고 확장성 있는 코드도 만들 수 있습니다.
  • Enum 내부에서는 values(), ordinal(), name() 같은 유용한 메서드들도 제공되어 활용도가 높습니다.

 

'TIL' 카테고리의 다른 글

객체를 못 만드는 클래스가 있다고요? 그게 바로 추상클래스  (2) 2025.04.23
계산기 Lv.3  (0) 2025.04.22
계산기 Lv.2  (0) 2025.04.17
계산기 Lv.1  (0) 2025.04.16
객체지향  (1) 2025.04.15