열거형 클래스인 Enum에 대해 알아보자.
Enum : 열거타입(enum) 이란 관련이 있는 상수의 집합의 클래스를 의미한다.
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) {
}
}
- 런타임 시점에서 둘 다 같은 상수이자 숫자일 뿐 구분할 수 없다.
- 이름, 번호가 같을 때 이름 충돌 방지를 위해서는 접두어를 써서 구분해야만 한다.
- 변수명에 쓰인 이름(문자열)을 출력하기 어렵다. -> switch 사용해야 함
- 같은 클래스에 속한 상수들을 순회(반복자/반복문)할 수 없다. (필드가 총 몇 개인지, 어떤 필드들이 있는지 알 수 없다.)
- 타입 안정성을 보장할 수 없다.
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 타입은 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 타입인 모든 요소들을 포함하고 있다고 생각하면 된다.