본문 바로가기

🧑🏻‍💻 Dev/Java

[Java] 열거형 (enum)

📘 열거형 (enums)

enum은 서로 관련된 상수를 편리하게 선언하기 위한 것으로 여러 상수를 정의할 때 사용하면 유용합니다. 즉 서로 관련이 있는 상수들을 모아 상수들을 대표할 수 있는 이름으로 타입을 정하는 것을 말합니다. 자바의 열거형은 열거형이 갖는 값뿐만 아니라 타입도 관리하기 때문에 논리적인 오류를 줄일 수 있습니다. 예를 들어 열거형을 모를 때 사용했었던 아래와 같은 int enum pattern이 있다고 생각해봅시다.

public class EnumEx {
    static final int APPLE = 0;
    static final int BANANA = 1;
    static final int ORANGE = 2;

    static final int MONKEY = 0;
    static final int BIRD = 1;
    static final int ELEPHANT = 2;

    public static void main(String[] args) {
        if (EnumEx.APPLE == EnumEx.MONKEY) {
            System.out.println("APPLE과 MONKEY는 같습니다.");
        }
    }
}

위에서 APPLE이라는 상수의 값도 0이고, MONKEY라는 상수의 값도 0입니다. 하지만 이는 값은 같지만 분명 다른 타입을 가지고 있는 상수입니다. APPLE은 과일이라는 타입의 상수이고, MONKEY의 경우에는 동물이라는 타입의 상수입니다. 위 코드의 출력 결과는 다음과 같습니다.

APPLE과 MONKEY는 같습니다.

이렇게 나오는 이유는 둘이 가지고 있는 값이 동일하기 때문에 조건문에서 참의 값을 갖습니다. 하지만 논리적으로는 “APPLE은 MONKEY이다 (사과는 원숭이다)”라는 말은 false가 나와야 맞습니다. 이것을 열거형을 사용해서 표현하면 논리적인 오류를 해결할 수 있습니다.

public class EnumEx {
    public enum Fruit {
        APPLE, BANANA, ORANGE
    }
    public enum Animal {
        MONKEY, BIRD, ELEPHANT
    }

    public static void main(String[] args) {
        if (EnumEx.Fruit.APPLE == EnumEx.Animal.MONKEY) {
            System.out.println("같습니다.");
        }
    }
}

위 처럼 enum을 이용해서 Fruit 타입과 Animal 타입을 나누어 주고, 실행을 해보면 다음과 같은 결과가 나옵니다.

java: incomparable types: EnumEx.Fruit and EnumEx.Animal

Fruit과 Animal은 비교할 수 없는 타입이라는 컴파일 에러가 발생합니다. 이렇게 enum을 사용하면 논리적인 오류를 막을 수 있습니다. enum의 사용 방법에 대해서 알아봅시다.

 

 

 

📖 enum 선언하기

1. 별도의 파일로 선언

// Job.java
public enum Job {
    STUDENT, TEACHER, DEVELOPER
}
// Person.java
public class Person {
    String name;
    int age;
    Job job;

    public static void main(String[] args) {
        Person person = new Person();
        person.name = "홍길동";
        person.age = 25;
        person.job = Job.STUDENT;

        System.out.println("이름은: " + person.name);
        System.out.println("나이는: " + person.age);
        System.out.println("직업은: " + person.job);
    }
}
이름은 홍길동
나이는 25
직업은 STUDENT

열거형을 별도의 파일에서 클래스 처럼 선언할 수 있다. 사용하는 것도 보면 Job이라는 열거형 타입을 선언하고, 값을 넣어서 사용합니다.

 

 

2. 클래스 내부에서 선언

public class Person {
    String name;
    int age;
    Job job;

    public enum Job {
        STUDENT, TEACHER, DEVELOPER
    }

    public static void main(String[] args) {
        Person person = new Person();
        person.name = "홍길동";
        person.age = 25;
        person.job = Job.STUDENT;

        System.out.println("이름은: " + person.name);
        System.out.println("나이는: " + person.age);
        System.out.println("직업은: " + person.job);
    }
}

 

 

3. 클래스 외부에서 선언

public class Person {
    String name;
    int age;
    Job job;

    public static void main(String[] args) {
        Person person = new Person();
        person.name = "홍길동";
        person.age = 25;
        person.job = Job.STUDENT;

        System.out.println("이름은: " + person.name);
        System.out.println("나이는: " + person.age);
        System.out.println("직업은: " + person.job);
    }
}

enum Job {
    STUDENT, TEACHER, DEVELOPER
}

위에 3가지 선언 방법이 있고, 출력되는 결과는 모두 동일한 것을 확인할 수 있습니다.

열거형의 특징은 다음과 같습니다.

  • 열거형으로 선언된 순서대로 0부터 인덱스 값을 가지게됩니다.
    • STUDENT는 0, TEACHER는 1, DEVELOPER는 2의 인덱스를 가지게 됩니다.


  • enum에 선언된 상수들은 모두 대문자로 작성해야 합니다.

  • 마지막 열거형 변수를 선언하고 세미콜론(;)을 찍지 않습니다.
    • 하지만 상수와 연관된 문자를 지정하는 경우에는 세미콜론(;)을 찍습니다.

 

 

 

📖 enum 메소드

대표적으로 사용되는 values(), ordinal(), valueOf()에 대해서 알아보겠습니다.

 

1. values()

열거된 모든 상수를 배열에 담아서 순서대로 리턴합니다.

public class Main {
    public static void main(String[] args) {
        for (Fruit fruit : Fruit.values()) {
            System.out.print(fruit + " ");
        }
    }
}

enum Fruit {
    PINEAPPLE, APPLE, WATERMELON, ORANGE
}
출력
PINEAPPLE APPLE WATERMELON ORANGE

 

 

2.  ordinal()

enum에 열겨된 순서를 정수형으로 반환

public class Main {
    public static void main(String[] args) {
        for (Fruit fruit : Fruit.values()) {
            System.out.println(fruit + "은 " + fruit.ordinal());
        }
    }
}

enum Fruit {
    PINEAPPLE, APPLE, WATERMELON, ORANGE
}

 

출력
PINEAPPLE은 0
APPLE은 1
WATERMELON은 2
ORANGE은 3

 

 

3. valueOf()

매개변수로 입력되는 문자열과 동일한 이름을 갖는 원소를 반환

public class Main {
    public static void main(String[] args) {
        Fruit apple = Fruit.valueOf("APPLE");
        System.out.println(apple);
    }
}

enum Fruit {
    PINEAPPLE, APPLE, WATERMELON, ORANGE
}

 

출력
APPLE

일치하지 않는 경우에는 IllegalArgumentException을 발생

public class Main {
    public static void main(String[] args) {
        Fruit apple = Fruit.valueOf("BANANA");
        System.out.println(apple);
    }
}

enum Fruit {
    PINEAPPLE, APPLE, WATERMELON, ORANGE
}
Exception in thread "main" java.lang.IllegalArgumentException: No enum constant Fruit.BANANA
  at java.base/java.lang.Enum.valueOf(Enum.java:240)
  at Fruit.valueOf(Main.java:10)
  at Main.main(Main.java:5)

 

 

 

📖 열거형 상수를 다른 값과 연결하기

위에서 열거형을 선언했을 때는 세미콜론(;)을 붙이지 않았습니다. 하지만 열거형 상수를 다른 값과 연결하는 경우에는 마지막 열거형 원소 뒤에 세미콜론(;)을 붙여야 한다.

public class Main {
    public static void main(String[] args) {
        for (Fruit fruit : Fruit.values()) {
            System.out.println(fruit + "의 한국어는 " + fruit.getName());
        }
    }
}

enum Fruit {
    PINEAPPLE("파인애플"),
    APPLE("사과"),
    WATERMELON("수박"),
    ORANGE("오렌지");

    private final String name;
    
    Fruit(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
출력
PINEAPPLE의 한국어는 파인애플
APPLE의 한국어는 사과
WATERMELON의 한국어는 수박
ORANGE의 한국어는 오렌지

 

위에서 열거형 원소 옆에 ()를 사용해서 연결되는 값을 입력할 수 있습니다. 이때 생성자를 사용해서 연결된 값을 넣어줄 수 있습니다.

여기서 알아둬야 하는 것은 enum 열거형 생성자의 제어자가 private으로 기본 설정되어 있다는 것입니다. 그렇기 때문에 enum 밖에서 생성자는 사용할 수 없습니다.

enum Fruit {
    PINEAPPLE("파인애플"),
    APPLE("사과"),
    WATERMELON("수박"),
    ORANGE("오렌지");

    private final String name;
    
    Fruit(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Fruit fruit = new Fruit("수박");  // 컴파일 에러

그리고 enum의 원소는 여러 개의 값과 연결될 수도 있습니다.

과일의 한국어 이름과 색깔을 연결된 값으로 가지고 있는 Fruit 열거형에 대해서 예를 들어보겠습니다.

public class Main {
    public static void main(String[] args) {
        for (Fruit fruit : Fruit.values()) {
            System.out.println(fruit + "의 한국어는 " + fruit.getName() + "이고, 색깔은 " + fruit.getColor() + "이다.");
        }
    }
}

enum Fruit {
    PINEAPPLE("파인애플", "노란색"),
    APPLE("사과", "빨간색"),
    WATERMELON("수박", "초록색"),
    ORANGE("오렌지", "주황색");

    private final String name;
    private final String color;
    Fruit(String name, String color) {
        this.name = name;
        this.color = color;
    }

    public String getName() {
        return name;
    }

    public String getColor() {
        return color;
    }
}
PINEAPPLE의 한국어는 파인애플이고, 색깔은 노란색이다.
APPLE의 한국어는 사과이고, 색깔은 빨간색이다.
WATERMELON의 한국어는 수박이고, 색깔은 초록색이다.
ORANGE의 한국어는 오렌지이고, 색깔은 주황색이다.

위 처럼 생성자에 하나의 매개변수가 더 들어가고, color 값을 얻어올 수 있는 getColor()라는 새로운 메소드를 추가해줬습니다. 이렇게 enum의 원소는 여러개의 값과 연결되어 사용될 수도 있습니다.