Java - Enum

제훈·2024년 7월 24일

Java

목록 보기
26/34

열거형 클래스인 Enum에 대해 알아보자.

Enum

Enum : 열거타입(enum) 이란 관련이 있는 상수의 집합의 클래스를 의미한다.

  1. 클래스처럼 보이게 하는 상수
  2. 서로 관련있는 상수들끼리 모아 상수들을 대표할 수 있는 이름으로 타입을 정의하는 것
  3. Enum 클래스 형을 기반으로 한 클래스형 선언

Enum 사용 X

Enum을 사용하지 않을 때 (= 정수 열거 패턴) 의 문제점들을 보자.

Subjects 인터페이스

public interface Subjects {
    // BACK
    public static final int JAVA = 0;
    public static final int MARIADB = 1;
    public static final int JDBC = 2;

    // FRONT
    public static final int HTML = 0;
    public static final int CSS = 1;
    public static final int JAVASCRIPT = 2;
}

Application

import java.util.Scanner;

public class Application {
    public static void main(String[] args) {
        int subjectOne = Subjects.JAVA;
        int subjectTwo = Subjects.HTML;

        // 인터페이스에서 변수명이 다름에도 값을 똑같은 0으로 넣어주면 아래 조건문이 true가 된다..
        // 즉, 1. 런타임 시점에서 둘 다 같은 상수이자 숫자일 뿐 구분할 수 없다.
        if (subjectOne == subjectTwo) {
            System.out.println("두 과목은 같은 과목이다.");
        }

        // 2. 이름 충돌 방지를 위해서는 접두어를 써서 구분해야만 한다. (이름, 번호가 같을 때)
        // JS는 프론트도 되고 백도 돼서 둘다 쓰고 싶다면 별도의 작업을 해줘야 한다. (코드는 X)

        // 3. 변수명에 쓰인 이름(문자열)을 출력하기 어렵다. - (feat.switch)
        Scanner scanner = new Scanner(System.in);
        System.out.print("과목번호를 입력해주세요. : ");
        int fieldNo = scanner.nextInt();
        String subName = "";
        switch(fieldNo) {
            case Subjects.JAVA: subName = "JAVA"; break;
            case Subjects.MARIADB: subName = "MARIADB"; break;
            case Subjects.JDBC: subName = "JDBC"; break;
        }

        System.out.println("선택한 과목명은 : " + subName + "입니다.");

        // 4. 같은 클래스에 속한 상수들을 순회(반복자/반복문)할 수 없다. (필드가 총 몇 개인지, 어떤 필드들이 있는지 알 수 없다.)

        // 5. 타입 안정성을 보장할 수 없다.
        printSubject(Subjects.JAVA);
        printSubject(0);        // 과목 상관없고 그냥 숫자의 개념도 허용된다.
    }

    private static void printSubject(int subjectsName) {

    }
}
  1. 런타임 시점에서 둘 다 같은 상수이자 숫자일 뿐 구분할 수 없다.
  2. 이름, 번호가 같을 때 이름 충돌 방지를 위해서는 접두어를 써서 구분해야만 한다.
  3. 변수명에 쓰인 이름(문자열)을 출력하기 어렵다. -> switch 사용해야 함
  4. 같은 클래스에 속한 상수들을 순회(반복자/반복문)할 수 없다. (필드가 총 몇 개인지, 어떤 필드들이 있는지 알 수 없다.)
  5. 타입 안정성을 보장할 수 없다.

Enum 사용 시

Subjects - Enum 클래스

public enum Subjects {
    JAVA,
    MARIADB,
    JDBC,
    HTML,
    CSS,
    JAVASCRIPT;

    Subjects() {
        System.out.println("기본 생성자");
    }

    @Override
    public String toString() {
        return "----" + this.name() + "----";
    }
}

Application

public class Application {
    public static void main(String[] args) {
        Subjects subjectOne = Subjects.JAVA;
        Subjects subjectTwo = Subjects.HTML;

        /* 설명. 1. 열거 타입으로 선언된 인스턴스는 싱글톤으로 관리되며 인스턴스가 각각 한 개임을 보장한다.
        *       작성한 순서에 따라 각각은 다른 인스턴스가 생성돼 최초 호출 시에 enum의 생성자를 활용해 생성된다.
        *       -> lazy singleton 개념 */
        if (subjectOne == subjectTwo) {
            System.out.println("두 과목은 같은 과목이다.");
        } else {
            System.out.println("서로 다른 과목이다.");
        }

        // 2. 싱글톤이기에 == 비교가 가능하다. (동일 객체 비교 가능)
        Subjects subjectThree = Subjects.JAVA;

        if (subjectOne == subjectThree) {
            System.out.println("싱글톤이다.");
        }

        // 3. 오버라이딩되지 않은 toString() 또는 name() 메소드를 활용해 필드명을 문자열로 변경하기 쉽다.
        // 쉽게 말해 switch 안 써도 됨 -> 개선사항
        System.out.println(Subjects.JAVA.toString());
        System.out.println(Subjects.JAVA.name());

        // 4. values()를 사용해 상수값 배열을 반환받고 이를 활용해 연속처리를 해줄 수 있다.
        Subjects[] subjects = Subjects.values();
        for (Subjects subject : subjects) {
            System.out.println(subject.toString());
            System.out.println(subject.ordinal());
        }

        // 5. 타입 안정성을 보장한다.
        printSubjects(Subjects.JAVA);
//        printSubjects(0);             //Subjects 타입이 아니어서 에러 발생
    }

    private static void printSubjects(Subjects subjects) {
    }

}


Enum 문법

enum 타입의 필드를 최소 사용 시에만 열거 타입의 인스턴스를 생성하고 생성자를 따로 호출하지 않는다.

또한 enum 타입은 set으로 바꿔 반복시키며, 필드들을 한 번에 확인하고 활용할 수 있다.

UserRole1 - Enum 클래스

public enum UserRole1 {
    GUEST,
    CONSUMER,
    PRODUCER,
    ADMIN;
}

Application

package com.ohgiraffers.section3.grammer;

public class Application {
    public static void main(String[] args) {
        UserRole1 admin = UserRole1.ADMIN;
        System.out.println(admin.name());
    }
}

위가 기본 세팅으로 되어 있을 때

메소드나 필드 추가

UserRole1에 단순한 메소드를 추가해보자.

public enum UserRole1 {
    GUEST,
    CONSUMER,
    PRODUCER,
    ADMIN;

    public String getNameToLowerCase() {
        return this.name().toLowerCase();
    }
}

Application

public class Application {
    public static void main(String[] args) {
        UserRole1 admin = UserRole1.ADMIN;
        System.out.println(admin.name());
        System.out.println(admin.getNameToLowerCase());
    }
}

실행결과


이번엔 메소드 말고 필드를 가져보자.

UserRole2 - Enum 클래스

public enum UserRole2 {
    GUEST("게스트"),
    CONSUMER("구매자"),
    PRODUCER("판매자"),
    ADMIN("관리자");

    private final String DESCRIPTION;

    UserRole2(String description) {
        DESCRIPTION = description;
    }

    public String getDescription() {
        return DESCRIPTION;
    }
}

Application

public class Application {
    public static void main(String[] args) {
        UserRole2 consumer = UserRole2.CONSUMER; // 이 순간에 매개변수 생성자를 이용해서 사용 가능
        System.out.println(consumer.ordinal() + ", " + consumer.name() + ", " + consumer.getDescription());
        
        System.out.println("Set 으로 바꿔서 반복자 사용해보기");
        EnumSet<UserRole2> roles = EnumSet.allOf(UserRole2.class);
        
        Iterator<UserRole2> iterator = roles.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next().name());
        }
    }
}

실행결과

.class 는 뭘까?
UserRole2 는 주소를 지칭하고
UserRole2.class 는 해당 타입만을 지칭한다.

즉, EnumSet.allOf(UserRole2.class)EnumSet의 요소들은 UserRole2 타입인 모든 요소들을 포함하고 있다고 생각하면 된다.


profile
백엔드 개발자 꿈나무

0개의 댓글