본문 바로가기
Java

[Java] Enum 사용법 (2) - 문법

by 노력남자 2022. 9. 6.
반응형

Enum 총 정리

Enum 탄생 배경

Enum 사용법

Enum 활용법

 

Enum 선언

enum 열거형이름 { 상수명1, 상수명2, …)

 

Enum 메서드

메서드 설명
Class<E> getDeclaringClass() 열거형의 Class 객체를 리턴한다.
String name() 열거형 상수의 이름을 문자열로 리턴
int ordinal() (웬만하면 사용 X) 열거형 상수가 정의된 순서를 리턴 (0부터 시작)
T valueOf(Class<T> enumType, String name)
T valueOf(String name)
[지정된 열거형에서] name 과 일치하는 열거형 상수를 리턴
T[] values() 열거형 상수들을 배열 형태로 리턴

 

예제) 

public class enumTest {
    public enum Season {
        SPRING, SUMMER, AUTUMN, WINTER
    }

    public static void main(String[] args) {
        Season.SPRING.getDeclaringClass(); // == Class<Season>

        Arrays.stream(Season.values()).forEach(value-> {
            System.out.println("ordinal : " + value.ordinal() + ", name : " + value.name());
        });

        // ordinal : 0, name : SPRING
        // ordinal : 1, name : SUMMER
        // ordinal : 2, name : AUTUMN
        // ordinal : 3, name : WINTER

        Season.valueOf("SPRING"); // == Season.SPRING
        Season.valueOf("JAVA"); // 없을 경우 IllegalArgumentException 발생
    }
}

 

웬만하면 ordinal()은 사용하지 말라는 이유?

 

ordinal()을 사용할땐 해당 상수가 enum에서 몇 번째 위치인지 판단하려고 사용하는데 아래와 같은 경우 문제가 생긴다.

 

public enum OrdinalEx {
	// 원래 상수 순서
	// ONE, TWO, THREE, FIVE, NINE, TEN 
	TEN, ONE, TWO, THREE, FIVE, NINE // 유지보수하면서 바뀐 상수 선언 순서
} 

 

의도한 상수 순서가 만약에 유지보수하면서 바뀐다면? ordinal()를 호출했던 로직들은 모두 깨질것이다.

 

그럼 어떻게 이 문제를 해결해야 할까? 뒤에서 배울 enum에 인스턴스 변수를 추가하는 방법으로 처리할 수 있다.

 

public enum OrdinalEx {
	TEN(10), ONE(1), TWO(2), THREE(3), FIVE(5), NINE(9)
    
	private final int value;
    
	OrdinalEx(int value) { this.value = value; }
    
	public int getValue() { return value; }
} 

* Enum의 API 문서를 보면 ordinal()은 "EnumSet, EnumMap 같은 열거 타입 기반의 범용 자료구조에 쓸 목적으로 설계되었다."라고 나와있다.

 

Enum 멤버 추가하기

Enum에 멤버를 추가하는 이유는 enum 상수와 연관된 데이터를 상수 자체에 포함시켜 용이하게 관리하기 위함이다.

 

(1) 인스턴스 필드 추가하기

 

인스턴스 필드를 추기하려면 enum 상수의 이름 옆에 원하는 값을 괄호()와 함께 적어주면 된다.

 

public enum SearchSite {
	NAVER("https://www.naver.com"),
	DAUM("https://www.daum.net"),
	GOOGLE("https://www.google.com"),
	BING("https://www.bing.com");

	private final String url; // 인스턴스 필드 추가

	// 생성자 추가
	SearchSite(String url) { this.url = url; }

	// 인스턴스 필드 get메서드 추가
	public String getUrl() { return url; }
}

// NAVER만 예로 들었습니다.
// public class SearchSite {
//     static final SearchSite NAVER = new SearchSite("NAVER", "https://www.naver.com");
//
//     private final String name;
//     private final String url;
//
//     SearchSite(String name, String url) {
//         this.name = name;
//         this.url = url;
//     }
// }

(*인스턴스 필드가 추가된 enum을 class로 표현하면 주석을 단 부분과 비슷하다.)

 

 

(2) 추상 메서드 추가하기

 

추상 메서드를 추가할 때 enum 상수가 해당 추상 메서드를 반드시 구현해야만 하게 해당 메서드에 abstract 제한자를 붙혀야 한다.

 

또한, 추상 메서드에서 enum 상수에 있는 변수를 참조하기 위해서 protected 제한자를 사용해야한다.

 

public enum Transportation {
	BUS(100) { int fare(int distance) { return distance * BASIC_FARE; } },
	TRAIN(150) { int fare(int distance) { return distance * BASIC_FARE; } },
	SHIP(200) { int fare(int distance) { return distance * BASIC_FARE; } },
	AIRPLANE(300) { int fare(int distance) { return distance * BASIC_FARE; } };

	protected final int BASIC_FARE; // 반드시 protected

	Transportation(int basicFare) {
		BASIC_FARE = basicFare;
	}

	public int getBasicFare() { return BASIC_FARE; };
    
	// 반드시 abstract로 설정 
	// 거리에 따른 요금 계산
	abstract int fare(int distance);
}

// BUS 예로 들었습니다.
// abstract public class Transportation {
//     static final Transportation BUS = new Transportation(100) {
//         @Override
//         int fare(int distance) {
//             return distance * BASIC_FARE;
//         }
//     };
// 
//     protected final int BASIC_FARE;
// 
//     Transportation(int basicFare) {
//         BASIC_FARE = basicFare;
//     }
// 
//     public int getBasicFare() { return BASIC_FARE; };
// 
//     abstract int fare(int distance);
// }

 

다음 포스팅에선 지금까지 공부했던 Enum을 실무에서는 어떻게 활용하는지 Enum 활용법에 대해 알아보겠습니다.

 

 

읽어주셔서 감사합니다.

 

참고

  • 남궁성, Java의 정석 3rd Edition, 도우출판, 2016
  • Joshua Bloch, Effective Java 3/E, 인사이트, 2018
반응형

댓글