열거형
열거형이란
서로 관련된 상수를 편리하게 선언하기 위한 것으로 여러 상수를 정의할 때 사용하면 유용하다.
자바 1.5전에는 C언어와 달리 별도의 열거형이라는 것이 존재하지 않았으나 1.5부터 새로 추가되었다.
enum은 여러 상수를 정의한 후, 정의된 것 이외의 값은 허용하지 않는다.
아래는 Enum이 컴파일러에 의해 정의되는것부터 Enum의 상수들이 바이트코드로 포함되고 이후
런타임때 클래스 로더에 의해 로드되는 과정을 아래 그림으로 표현했다
사용 이유와 장점
- 코드 가독성 향상: Enum은 관련된 상수들을 그룹화하고 이름을 부여하여 코드의 가독성을 향상시킵니다. 이 경우, 각 단위(Unit)는 Enum 상수로 정의되어 있으며, 코드에서 해당 상수를 사용하면 의미가 명확하게 전달됩니다.
- 타입 안정성(Type Safety): Enum을 사용하면 컴파일러가 타입 안정성을 검사할 수 있으므로 잘못된 단위나 값을 사용하는 오류를 미리 방지할 수 있습니다.
- 상수값 관리: Enum을 사용하면 관련된 상수 값을 한 곳에서 중앙 관리할 수 있습니다. 이 코드에서는 각 단위의 이름과 Enum 값이 매핑되어 있으므로, 단위의 이름을 변경해야 할 때 Enum 값만 수정하면 됩니다.
- 코드 유지 및 확장 용이성: 새로운 단위를 추가하거나 기존 단위를 수정하기 쉽습니다. Enum에 새로운 상수를 추가하거나 수정하면 코드의 다른 부분에서 변경이 필요하지 않습니다.
- 코드 품질 향상: Enum을 사용하면 상수와 그에 따른 동작을 묶어서 정의할 수 있으므로, 코드의 일관성을 유지하고 오류 가능성을 줄일 수 있습니다.
단점
- 번거로운 선언: 열거형 상수를 선언할 때 모든 상수를 나열해야 하며, 상수가 많은 경우 선언이 번거로울 수 있습니다.
- 확장성의 한계: 열거형은 런타임 중에 새로운 상수를 동적으로 추가하는 것이 불가능합니다. 따라서 Enum에 새로운 상수를 추가하려면 소스 코드를 수정하고 다시 컴파일해야 합니다.
- 메모리 사용량: 열거형은 각 상수가 하나의 인스턴스이므로 모든 상수가 메모리에 상주하게 됩니다. 이는 Enum이 상수 개수가 많은 경우 메모리 사용량을 높일 수 있습니다.
- 상속의 제한: 열거형은 다른 클래스를 상속할 수 없습니다. Java에서는 단일 상속만 허용하므로 열거형은 이미 다른 클래스를 상속할 수 없습니다.
- 유연성 부족: Enum은 정적이며 상수가 고정되어 있기 때문에 동적으로 변하는 데이터를 표현하기에는 적합하지 않습니다.
- 복잡한 로직의 표현 어려움: 열거형은 주로 상수 집합을 표현하는 데 사용되며, 복잡한 로직이나 동작을 표현하기에는 제한적일 수 있습니다.
Enum정의하는 법
jdk1.5이전 열거형처럼 사용할려면 아래와 같이 코드를 짜야했다.
public class Day {
public static final int MONDAY = 1;
public static final int TUESDAY = 2;
public static final int WEDNESDAY = 3;
public static final int THURSDAY = 4;
public static final int FRIDAY = 5;
public static final int SATURDAY = 6;
public static final int SUNDAY = 7;
}
정의와 사용
- enum 열거형 이름{‘상수명1, 상수명2}
public enum Day {
MONDAY, //대문자가 관례
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY;
}
enum 상수형으로 선언 하기
import enum_example.Day;
public class Main {
public static void main(String args[]) {
Day today = Day.MONDAY; //Day형 today를 Day형 상수 Monday로 선언
String str = today.toString();
System.out.println(str);
System.out.println(today); //알아서 toString()변환해줌
}
}
결과
한가지 알아둬야할게 출력문에서 Enum의 상수를 지칭하는 Enum형 변수가 출력문에 들어가면 자동으로 문자열로 바뀐다는것이다.(처음에 today를 문자열로 안바꾸고 출력했는데 에러가 안떠서 당황)
열거형 비교
열거형 상수간의 비교에는 '=='를 사용할 수 있다. '=='로 비교한다는것은 빠른 성능을 제공한다는 뜻이다.
compareTo 도 사용 가능하지만 '<','>'같은 비교연산자는 사용불가능하다 . compareTo()는 비교대상이
같으면 0, 왼쪽이 크면 양수, 오른쪽이 크면 음수를 반환한다
Enum클래스내의 CompareTo메서드
상수 정의된 순서를 구하고자 ordinal메서드를 사용하는 것을 알 수있다. 아래에서 설명할 예정
CompareTo 사용예시
import enum_example.Day;
public class Main {
public static void main(String args[]) {
Day today = Day.TUESDAY;
Day tomorrow = Day.WEDNESDAY;
// compareTo 메서드를 사용하여 두 Enum상수를 비교
int comparison = today.compareTo(tomorrow);
if (comparison < 0) {
System.out.println(today+"기준으로 "+tomorrow+"는 내일이다");
} else if (comparison > 0) {
System.out.println(today+"기준으로 "+tomorrow+"는 어제이다");
} else {
System.out.println(today+"기준으로 "+tomorrow+"는 같은 요일이다");
}
}
}
출력
+switch문에도 사용이 가능하다
public class Main {
public static void main(String[] args) {
DayOfWeekEnum today = DayOfWeekEnum.MONDAY;
switch (today) {
case MONDAY:
System.out.println("오늘은 월요일입니다.");
break;
case TUESDAY:
System.out.println("오늘은 화요일입니다.");
break;
case WEDNESDAY:
System.out.println("오늘은 수요일입니다.");
break;
default:
System.out.println("올바른 요일이 아닙니다.");
break;
}
}
Enum이 제공하는 메서드
4가지
- Class<E> getDeclaringClass(): 열거형의 객체를 반환한다
- String name(): 이름을 문자열로 반환
- int ordinal(): 열거형 상수가 정의된 순서를 반환한다
- T valueOf(Class<T> enum Type, String name): 지정된 열거형에서 name과 일치하는 열거형 상수를 반환한다
Enum클래스 문서
사용 예시
Class getDeclaringClass()
- 좋은 예시는 아니지만 이런식으로 enum이 속해있는 클래스를 알려준다
import enum_example.Day;
import enum_example.PizzaStoreClosureDay;
public class Main {
public static void main(String args[]) {
Day today = Day.WEDNESDAY;
Day tomorrow = Day.THURSDAY;
PizzaStoreClosureDay store1ClosureDay1 = PizzaStoreClosureDay.WEDNESDAY;
Class<? extends Enum> declaringClass = store1ClosureDay1.getDeclaringClass(); //대체 휴무일이 수요일인 상점1의 클래스는 어디냐? 피자집이여 치킨집이여?
System.out.println("휴무일에 수요일이 속해있는 클래스는 "+declaringClass+" 이다");
}
}
String name()
import enum_example.Day;
public class Main {
public static void main(String args[]) {
Day today = Day.WEDNESDAY;
Day tomorrow = Day.THURSDAY;
String dayName = Day.MONDAY.name();
System.out.println(dayName);
}
}
ordinal
import enum_example.Day;
public class Main {
public static void main(String args[]) {
Day today = Day.WEDNESDAY;
Day tomorrow = Day.THURSDAY;
int dayNum = Day.FRIDAY.ordinal();
System.out.println(dayNum);
}
}
T valueOf(Class enum Type, String name)
import enum_example.Day;
public class Main {
public static void main(String args[]) {
String todayStr = "WEDNESDAY"; //오늘 요일을 문자열로 선언
Day today = Enum.valueOf(Day.class, todayStr); //해당하는 Enum클래스와 문자열대입
System.out.println(today);
}
}
그렇다면 enum에 해당하지 않는 문자열이 들어가있다면 어떻게 될까? todayStr ="WEDNESDA" - 일부러 Y뺌
아래와 같이 해당하는 enum이 없다고 뜬다
추가
values() 메서드
- 이 메서드를 호출하면 Enum 클래스에 정의된 모든 상수가 배열로 반환되며, 배열의 순서는 Enum 상수가 정의된 순서와 동일하다. Enum상수를 반복할때 유용하다
import enum_example.Day;
public class Main {
public static void main(String[] args) {
Day[] days = Day.values();
for (Day day : days) {
System.out.println(day);
}
}
}
java.lang.Enum
자바의 java.lang.Enum 클래스 문서를 보면 java.lang.Enum 클래스가 모든 Java 언어 열거형 타입의 공통 기본 클래스임을 나타낸다. 즉, 모든 열거형 타입은 이 클래스를 기반으로 하여 상속받는다
따라서 Enum클래스를 상속받고 본인만의 Enum클래스 형식을 만드는 것도 가능하다
아래 블로그에 가보면 자세한 예시가 나와있다
참고: https://b-programmer.tistory.com/262
EnumSet
EnumSet은 enum클래스와 함께 작동하는 특수 SEt컬렉션이다. Set 인터페이스를 구현하고 AbstractSet에서 확장된다
참고: https://www.baeldung.com/java-enumset
고려사항
1. 열거형 값만 포함 할 수 있으며, 모든 값은 동일한 열거형이어야 한다.
2. null을 추가할 수 없다.
EnumSet 사용 예제
EnumSet생성
import enum_example.Day;
import java.util.EnumSet;
public class Main {
public static void main(String args[]) {
// EnumSet 생성
EnumSet<Day> workingDays = EnumSet.of(Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY, Day.THURSDAY, Day.FRIDAY);
//순회
for (Day day : workingDays) {
System.out.println(day);
}
}
}
add, remove 예시EnumSet에 들어가있는 열거형에 해당하는 enum을 추가하거나 제거할수 있다
import enum_example.Day;
import java.util.EnumSet;
public class Main {
public static void main(String args[]) {
// EnumSet 생성
EnumSet<Day> workingDays = EnumSet.of(Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY, Day.THURSDAY, Day.FRIDAY);
//일하는 요일중에 월요일이 토요일로 변경되었다고 가정
workingDays.add(Day.SATURDAY);
workingDays.remove(Day.MONDAY);
//순회
for (Day day : workingDays) {
System.out.println(day);
}
}
}
allOf(), noneOf()예시allOf: 열거형의 모든 요소를 포함하는 EnumSet생성noneOf: 빈 열거형을 갖는 EnumSet생성
import enum_example.Day;
import java.util.EnumSet;
public class Main {
public static void main(String args[]) {
// EnumSet 생성
EnumSet<Day> antWorkingDays = EnumSet.allOf(Day.class);
EnumSet<Day> baeksu = EnumSet.noneOf(Day.class);
//순회
System.out.println("개미 출근 요일");
for (Day day : antWorkingDays) {
System.out.println(day);
}
System.out.println("\n백수 출근 요일");
for (Day day : baeksu) {
System.out.println(day);
}
}
}
이외에도 포함되어있는 특정 요소를 찾는 contains(), 다른 EnumSet의 모든 요소를 복사하는 copyOf()등의 메서드가 있다
describeConstable()메서드
공식문서 참고: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Enum.html
자바 12에 추가되었고 인스턴스에 대한 열거형 설명자를 EnumDesc생성할 수 있으면 반환하고, 생성할 수 없으면 빈 값을 반환한다
아래는 사용예시다
package enum_example;
import java.util.Optional;
public enum Day {
MONDAY("한주의 시작"),
TUESDAY("주의 이틀차"),
WEDNESDAY("한주의 중간"),
THURSDAY("한주의 중간이 지나감"),
FRIDAY("평일의 끝"),
SATURDAY("주말"),
SUNDAY("주말");
private final String description;
Day(String description) {
this.description = description;
}
public Optional<Enum.EnumDesc<Day>> describeConstable() {
return Optional.of(Enum.EnumDesc.of(name(), description));
}
public static void main(String[] args) {
Day day = Day.WEDNESDAY;
// describeConstable() 메서드를 사용하여 상수 설명을 얻음
Optional<Enum.EnumDesc<Day>> description = day.describeConstable();
// 설명이 있는 경우 출력
description.ifPresent(desc -> {
System.out.println("Day: " + desc);
});
}
}
참고
자바의 정석(저자 남궁인)
오라클 공식문서
https://five-cosmos-fb9.notion.site/Enum-6ffa87530c424d8ab7a1b585bfb26fa2
https://b-programmer.tistory.com/262
'자바(Java)' 카테고리의 다른 글
[자바] 자바 I/O (1) | 2023.10.14 |
---|---|
[자바] 자바 어노테이션 (1) | 2023.10.05 |
[자바] 자바 멀티쓰레드 프로그래밍 (0) | 2023.09.21 |
[자바] 자바 예외처리 (0) | 2023.09.14 |
[자바] 자바 인터페이스 (0) | 2023.09.08 |