Enum
상수 컬렉션을 정의하는데 사용하는 특수한 자바 유형이다.
Enum 클래스로 상수를 정의할 때에는 다음과 같이 정의를 한다.
enum Constants { AA, BB, CC, DD }
enum에 접근 할 때에는 Constants.AA 와 같이 스태틱 변수에 접근하는 것처럼 접근을 할 수 있고, 각 값에 대해서 ordinal 순서에 대한 값이 생긴다. 어떻게 생기는 것일까?
위에 힌트가 있다. 자세히 살펴보면 Enum 클래스라고 명시를 했는데, 선언을 하는 것을 보니 enum이라고 되어 있다.
사실 enum은 Enum 클래스를 생성하는 특수한 방식이다.
Enum 클래스의 생성자를 살펴보면 그 비밀을 알 수 있다.
/**
* Sole constructor. Programmers cannot invoke this constructor.
* It is for use by code emitted by the compiler in response to
* enum type declarations.
*
* @param name - The name of this enum constant, which is the identifier
* used to declare it.
* @param ordinal - The ordinal of this enumeration constant (its position
* in the enum declaration, where the initial constant is assigned
* an ordinal of zero).
*/
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
해석하면 Enum의 생성자는 단독 생성자로, 프로그래머는 해당 생성자를 호출 할 수 없다. 이는 enum의 유형 선언 시에 사용되고 그에 대한 응답으로 컴파일러에서 내보낸 코드가 사용한다.
라고 되있는 것을 볼 수 있다. 즉, java.lang 패키지에 Enum 클래스가 enum 선언 유형에서 상수들에 대해서 정의를 해주는 것이다.
지금 생성자를 보니 Enum은 클래스이고 각 Enum에 대한 상수들은 AA, BB, CC, DD 형식으로 저장이 되었다.
여기서 보고 지나칠 수 있는 enum의 비밀을 하나 알게되었다. Enum은 상수 객체라는 것이다.
enum에 있는 상수들은 각각 객체이기 때문에, 결국 Enum 클래스에 들어있는 메소드들을 사용할 수 있다.
Enum 클래스에서 사용되는 메서드들에는 많은 것들이 사용되는데 대표적인 것만 살펴보자.
- getDeclaringClass() : 열거형 클래스의 이름을 반환한다. 여기서 형태는 class 패키지.enum 클래스명 으로 반환한다.
- name() : 열거형 상수의 이름을 문자열로 반환한다.
- ordinal() : 열거형 상수가 정의된 순서를 반환한다.
- T valueOf(Class<T> enumType, String name) : enum클래스에서 name과 일치하는 상수 객체를 반환한다.
하지만 상수 객체 하나 하나를 적어가면서 확인하기에는 쉽지 않을 것이다. 하지만 자바는 선언된 enum에 있는 모든 상수를 반환하는 메소드를 제공한다.
- enum이름.values()
이제 우리는 어떠한 열거형의 모든 상수들에 대한 정보를 확인할 수 있는 것이다. 다음과 같이 말이다.
for(Constants c : Constants.values()) {
System.out.println("=======================================");
System.out.println("상수의 클래스 이름 : " + c.getDeclaringClass());
System.out.println("상수의 이름 : " + c.name());
System.out.println(Enum.valueOf(Direction.class, d.name()).name());
System.out.println(d.toString()); // 상수의 toString()은 name() 으로 나온다.
System.out.println("=======================================");
}
이제 Enum 클래스에 어떻게 변수들이 선언되는지에 대해서 알아보자.
Enum 객체의 멤버 추가
결론부터 얘기하자면 Enum 객체에 멤버를 추가하는 것은 enum에 인스턴스 변수를 선언하는 것이다.
하지만 인스턴스 변수를 선언하고 나면 Enum 상수 객체들에 대해 컴파일러가 오류를 잡아 낼 것이다. 이러한 Enum 상수 객체들이 해당 enum에 선언된 인스턴스 변수를 가지기 위해서는 생성자를 추가해주어야 한다. 다음과 같이 말이다.
enum Constants {
AA(3), BB(4), CC(5), DD(6);
int value;
Constants(int value) {
this.value = value;
}
}
달라진 점이 있다면 기존에는 Enum 객체 생성시에 마지막에 세미콜론(;) 을 사용하지 않았는데, 이제는 사용을 해야한다.
우리는 이전에 위에 있는 생성자에서 각 Enum 객체가 생성되는 생성자를 살펴볼 수 있었다.
그리고 여기서 몇 가지 알아낸 것에 대해서 먼저 얘기를 해보겠다.
Enum 객체의 사용 범위
먼저 enum 유형에서 변수를 선언할 수 있는데 과연 해당 변수는 어디까지 유효한가에 대해서 테스트를 해봤다.
그 결과, enum 유형이 선언된 해당 패키지 내에서만 사용이 가능하다는 것을 알 수 있었다.
따라서 인스턴스 변수를 선언할 때, public 까지 접근 제어자를 지정하는데에는 아무런 문제가 없으나, 사실 default가 최대라는 사실을 알 수 있었다. 상수 객체에 접근하기 위해서는 결국 enum 이름으로 접근 해야하는데, 다른 패키지에서는 접근을 하지 못하니 말이다.
하지만, 상수 객체에 대한 값이 자주 변경되는 일은 없는 것이 좋을 것이다. 따라서 private 로 접근 제어자를 지정하고 Getter를 사용하는 것이 좋을 것 같다.
물론, Setter를 지정해서 상수 객체에 저장되어있는 값을 변경해주는 것 또한 불가능 한 부분이 아니다.
이걸 보고 이런 생각이 들을 수 있다.
enum 유형에 Getter와 Setter 를 사용한다고요? 메소드도 선언이 가능한건가요?
그렇다. enum 유형에는 Enum 객체에서 사용 할 수 있는 메소드까지 사용 할 수가 있다.
선언도 우리가 기존의 클래스에서 메소드를 생성하는 방식과 크게 다르지 않다.
enum Constants {
AA(3), BB(4), CC(5), DD(6);
private int value;
Constants(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
지극히 우리가 자주 보던 모습과 동일하다.
그럼 여기서 더 나아가 다음 생각까지 하는 사람도 있을 것 같다.
메소드의 선언을 하는 것이 가능하다면 추상 메소드의 선언도 가능 한 것인가?
이 부분은 정말로 신기했다. 무려 추상 메소드까지 선언하는 것이 가능하다. 굳이 enum 클래스를 가지고 추상 메소드까지 선언해서 이를 구현 할 일이 많을까 생각해봤는데 굳이 많이 사용 할 것이라는 생각이 들지 않는다. 사용한다면 대부분의 경우 Enum 상수 객체들에 대한 변수를 선언하고 접근하기까지가 전부 아닐까하지만, 그래도 사용 방법만 보자.
enum Constants {
AA(3) {
@Override
int multiple(int mul) {
return value * mul ;
}
},
BB(4) {
@Override
int multiple(int mul) {
return value * mul ;
}
},
CC(5) {
@Override
int multiple(int mul) {
return value * mul ;
}
},
DD(6) {
@Override
int multiple(int mul) {
return value * mul ;
}
};
private int value;
Constants(int value) {
this.value = value;
}
public int getValue() {
return value;
}
abstract int multiple(int mul);
}
진짜 신기하지 않은가, 추상 클래스여서 서로 다른 로직을 넣어도 문제없이 정상적으로 동작하지만, 지금은 간단히 보여주기 위해 같은 로직을 사용하였다.
Enum의 대한 사용을 좀 더 무궁무진하게 사용할 수 있는데, 다음 우아한 형제들 블로그에 Enum 활용기가 자세히 나와있었다.
Enum 클래스 자체에서 매개 변수를 함수형 인터페이스를 선언해서, 람다를 활용하고, 이에 따라 외부에서도 스트림을 활용하여
자바 JDK 1.8 이후로 사용되는 주된 기능들을 놀랍게 사용하고 있었다.
하루키의 법칙으로 배달의 민족 리드 개발자가 되다. 라는 유튜브를 보셨을지 모르겠다.
현재는 인프런에 계신 닉네임 향로 라고 불리시는 이동욱님의 Enum 활용기이다. 한 번 추가적으로 살펴보는 것도 도움이 많이 될 것 같다.
해당 Enum 활용에 대해서 이펙티브 자바라는 책에서 많은 도움을 얻었다는 글 내용을 볼 수 있었는데, 여유가 생기면 얼른 읽어보고 싶은 마음이다.
'Language > Java' 카테고리의 다른 글
가비지 콜렉션(Garbage Collection) (0) | 2023.04.22 |
---|---|
JVM (0) | 2023.04.21 |
7-7 내부 클래스 (재업로드), 익명 클래스 (0) | 2023.03.29 |
12. Generic 제너릭 (0) | 2023.03.29 |
4-2 자바 반복문 (0) | 2023.03.23 |