프로그래밍 언어/Java

[Java] Enum

highright96 2021. 12. 1.

Enum이란?

enum(열거형)은 열거형 클래스이며, 자바 1.5부터 사용이 가능하다.

 

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


자바의 열거형은 C언어의 열거형보다 더 향상된 것으로 열거형이 갖는 값뿐만 아니라 타입도 관리하기 때문에 보다 논리적인 오류를 줄일 수 있다.

 

자바 외 언어들에서는 타입이 달라도 값이 같으면 조건식 결과가 참(true)인 경우가 있으나, 자바의 열거형은 타입에 안전한 열거형(typesafe enum)이라서 실제 값이 같아도, 타입이 다르면 컴파일 에러가 발생한다.

 

Enum을 사용하는 이유

Enum을 잘 사용하면 코드의 가독성을 높이고 논리적인 오류를 줄일 수 있다.

 

학생 이름을 입력받으면 시험 점수를 알려주는 프로그램의 학생 이름을 상수로 관리한다고 했을 때의 문제점을 알아보자.

public class EnumDemo {
    //A학교
    private static final int KIM = 1;
    private static final int NAM = 2;
    private static final int PARK = 3;
    
    //B학교
    private static final int KIM = 4; //컴파일 에러
    private static final int NAM = 2; //A학교 NAM과 중복

    public static void main(String[] args) {
        int name = KIM;
        switch(name){
            case KIM:
                System.out.println("학생 KIM는 100점입니다.");
                break;
            case NAM:
                System.out.println("학생 NAM는 70점입니다.");
                break;
            case PARK:
                System.out.println("학생 PARK는 50점입니다.");
                break;
        }
    }
}

위 코드의 문제점은 다음과 같다.

  1. 각 상수에 부여된 1,2,3이라는 값은 논리적으로 아무런 의미가 없다. 즉 학생 KIM와 1은 아무런 관련이 없다.
  2. 이름의 충돌이 생길 수 있다. 만약 프로그램의 크기가 커 저서 다른 학교의 학생들도 관리한다고 가정해보자. A라는 학교에 KIM이라느 학생이 있고 B라는 학교에도 KIM이라는 학생이 있으면 이름이 중복되기 때문에 에러가 발생한다.
  3. 학교가 서로 다른 학생들끼리는 시험 점수가 비교되면 안되는데 학교 A의 학생 NAM과 학교 B의 학생 NAM 모두 int형 자료형이고 상수값이 동일하기 때문에 비교가 가능하다. 애초에 비교하는 코드를 작성할 수 없게 컴파일 단계에서 막아줘야 한다.

한정된 값(학생 이름)을 Enum(열거형)으로 관리한다면 위와 같은 문제점들을 해결해주고, 더욱 간단히 선언할 수 있도록 만든다.

 

Enum 정의

문법

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

 

예제

enum Job { TEACHER, STUDENT; }

 

Enum 사용 방법

문법

열거형이름.상수이름

 

예제

Job.TEACHER

 

Enum 특징

Enum에 정의된 상수들은 해당 Enum 타입의 객체이다

C 등의 다른 언어에도 열거형이 존재한다. 하지만 다른 언어들과 달리 Java의 enum은 단순한 정수 값이 아닌 해당 enum 타입의 객체이다.

enum Job { TEACHER, STUDENT; }

위와 같이 정의된 열거형은 아래와 같이 표현할 수 있다.

class Job {
    public static final Job TEACHER = new Job("TEACHER");
    public static final Job STUDENT = new Job("STUDENT");
    
    private String name;
    
    private Job(String name) {
        this.name = name;
    }
}

물론 실제 Enum의 구현체와 다르지만, 이런 형태로 생각하면 이해하기 더욱 쉽다.

 

생성자와 메소드를 추가할 수 있다

enum Job {
    TEACHER,
    STUDENT;

    Job(){
        System.out.println(this.name() + " 생성자 호출");
    }
}

public class EnumDemo {
    public static void main(String[] args) {
        Job student = Job.STUDENT;
    }
}
TEACHER 생성자 호출
STUDENT 생성자 호출

생성자를 정의할 수 있는데, enum의 생성자의 접근제어자는 private이기 때문에 외부에서 상수를 추가할 수 없다.

 

열거형의 멤버 중 하나를 호출하면, 열거된 모든 상수의 객체가 생성된다. 위 예시를 보면 STUDENT 하나를 호출했는데 열거된 모든 상수의 생성자가 호출되었음을 확인할 수 있다. 상수 하나당 각각의 인스턴스가 만들어지며 모두 public static final이다.

 

생성자를 이용해서 상수에 데이터를 추가할 수 있다

enum Job {
    DOCTOR(5000),
    TEACHER(1500),
    NURSE(1000);

    private final int dailyWages; //일급

    Job(int dailyWages){
        this.dailyWages = dailyWages;
    }

    public int getDailyWages() {
        return dailyWages;
    }
}

public class EnumDemo {
    public static void main(String[] args) {
        System.out.println("----일급----");
        System.out.println("DOCTOR : " + Job.DOCTOR.getDailyWages());
        System.out.println("TEACHER : " + Job.TEACHER.getDailyWages());
        System.out.println("NURSE : " + Job.NURSE.getDailyWages());
    }
}
----일급----
DOCTOR : 5000
TEACHER : 1500
NURSE : 1000

 

메소드에 switch문을 사용해 상수에 따라 다른 로직을 실행시킬 수 있다.

public double getMonthlyWages(int day) //상수에 따라 다르게 측정되는 월급
{
    switch (this) {
        case DOCTOR:
            return dailyWages * day * 2.5;
        case TEACHER:
            return dailyWages * day * 2;
        case NURSE:
            return dailyWages * day * 1.5;
        default:
            return 0;
    }
}

Enum Job에 위의 메소드를 추가한 뒤 아래와 같이 출력하면

System.out.println("----월급----");
System.out.println("DOCTOR : " + Job.DOCTOR.getMonthlyWages(30));
System.out.println("TEACHER : " + Job.TEACHER.getMonthlyWages(30));
System.out.println("NURSE : " + Job.NURSE.getMonthlyWages(30));

다음과 같은 결과가 나오는 것을 확인할 수 있다.

----월급----
DOCTOR : 375000.0
TEACHER : 90000.0
NURSE : 45000.0

 

추상 메서드를 선언해서 위와 동일하게 구현할 수 있다.

enum Job {
    DOCTOR(5000){
        @Override
        double getMonthlyWages(int day) {
            return getDailyWages() * day * 2.5;
        }
    },
    TEACHER(1500){
        @Override
        double getMonthlyWages(int day) {
            return getDailyWages() * day * 2;
        }
    },
    NURSE(1000){
        @Override
        double getMonthlyWages(int day) {
            return getDailyWages() * day * 1.5;
        }
    };

    private final int dailyWages; //일급

    Job(int dailyWages){
        this.dailyWages = dailyWages;
    }

    public int getDailyWages() {
        return dailyWages;
    }

    abstract double getMonthlyWages(int day); //상수에 따라 다르게 측정되는 월급
}



Enum 상수 간의 비교가 가능하다.

열거형 상수간의 비교에는 == 을 사용할 수 있다. equals()가 아닌 == 로 비교가 가능하다는 것은 그만큼 빠른 성능을 제공한다는 뜻이다.

Job doctor = Job.DOCTOR;
if(doctor == Job.DOCTOR){
    ...
} else if(doctor.compareTo(Job.NURSE) > 0){
    ...
}

 

참고

'프로그래밍 언어 > Java' 카테고리의 다른 글

[Java] 동일성(identity)과 동등성(equality)  (0) 2021.12.21
[Java] 오버로딩과 오버라이딩  (0) 2021.12.21
[Java] Interface  (0) 2021.12.01
[Java] Generic  (0) 2021.11.24
[Java] Reflection  (0) 2021.11.21

댓글