🇰🇷 Kor: 지금 보는 중!
🗺 Eng: Not yet!
🇯🇵 Jap: まだです。
🪖 "enum은 개인주의야."
자바에서 enum
은 열거 타입이라고 해서, 여러 선택지를 나열한 특별한 클래스 유형이죠. 그래서 기본적으로 서로 구분되는 유니크 상태입니다. 서로 구분되는 상태나 역할 등을 표현하는 데에 좋습니다.
public enum AccountStatus {
PENDING,
ACTIVE,
PROTECTED,
SUSPENDED,
SLEPT,
REMOVED
}
약간 더 구체적인 작성 양식은 이렇죠. 아마 이 글을 보시는 분들은 보통 아실 것 같습니다!
public enum 클래스_식별자 { // PascalCase
열거상수(생성자가 없다면 소괄호 생략), // UPPER_CASE
열거상수(생성자가 없다면 소괄호 생략),
...
세미콜론이 나올 때까지 콤마로 구분하여 열거상수 나열
... ;
// 세미콜론 이후로 일반 클래스처럼 필드 등을 작성합니다.
// 생성자는 외부에서 사용할 수 없습니다.
}
필드를 준 예시는 이렇겠죠. (@RequiredArgsConstructor
는 생성자를 만들어 주는 롬복 애노테이션)
@RequiredArgsConstructor
public enum AccountStatus {
PENDING(false),
ACTIVE(true),
PROTECTED(true),
SUSPENDED(true),
SLEPT(true),
REMOVED(false);
private final Boolean canSignIn;
public Boolean canSignIn() {
return canSignIn;
}
}
일반 클래스와 다르게 생성자가 static statements 블록보다 장유유서합니다.
자바의 클래스에는 클래스 로드 타임에 동작하는 정적 블록이 있습니다.
// 이렇게 생겼고요, 아는 분들은 다 아는데 모르는 분들은 말 안 해주면 모르는 그런 겁니다.
static {
// 여기에 실행문 쓰면 클래스가 처음 쓰일 때 딱 한 번 실행됩니다.
System.out.println("클래스가 메모리에 올라갈 때 실행되는 공간이오.");
}
클래스 로드 타임에 동작하니까, 동작 시점은 이렇게 되는 게 당연했어요:
클래스 "에헴, 메모리에 오르겠노라."
정적 블록 "(나는 이때 수행되어야 하는, 클래스 소유의 실행문이오.) 두둥!"
enum
도 클래스니까 정적 블록이 있는데, enum
클래스는 나열된 상수가 없으면 완성될 수 없기 때문에, 열거 상수를 만드는 각 '생성자'가 'static statements block'보다 먼저 실행됩니다.
클래스(enum) "에헴, 메모리에 오르겠노라."
열거상수들 "나도 나도 나 돋 나도나도도도 나도. 우리 없이 너네가 뭐 돼?" (생성자)
정적 블록 "선배님들 먼저 오르시지 말입니다..."
그래서 정적 블록이 실행될 때 이미 열거 상수는 생성되어 있어요. 물론 필드도요. 그래서 정적 블록에서 필드를 확인할 수 있다는 특징이 있습니다.
필드에 대한 필터는 static
블록에서 작성하면 됩니다:
static {
for (var item : values()) {
// 각 열거 항목(열거 상수)의 필드를 확인할 수 있습니다.
System.out.println("선배님들 딱 대시지 말입니다: " + item.field);
}
}
이제 유니크 필터를 간단히 씌워 봅시다.
Enum의 하위 필드의 유니크 필터
static
블럭을 사용한다는 점이 특징일 뿐, 그 내부에서 구현은 거창한 것 없이 그냥 유니크하게 관리하면 됩니다. 간단하게는 Set
타입을 사용해 중복을 체크할 수 있겠죠.
자바가 원래 클래스로드 타임은 동시성을 보장하니까 Concurrent
계열(ex: ConcurrentSkipListSet<...>
)을 안 써도 됩니다.
public enum Bits {
MaskA(1),
MaskB(1 << 1),
MaskC(1 << 2),
MaskD(1 << 3),
MaskE(1 << 4),
MaskF(1 << 5);
MaskG(1 << 6);
// Here
static {
Set<Integer> valueSet = new HashSet<>();
// values()에서 모든 열거 상수를 가져옵니다.
for (var enumItem : values()) {
// Set에서는 add 결과가 boolean이죠. (값이 잘 추가되면 true)
if (!valueSet.add(enumItem.value)) {
// 예시에서는 assert 키워드보단 Error를 던지는 걸 택했습니다.
throw new Error("중복되는 값이 없어야 합니다.");
}
}
}// static
private final int value;
Sample(int value) {
this.value = value;
}
public int value() {
return value;
}
}
Set
타입 데이터에 중복된 값이 add
되면 add
결과가 false
로 나오면서, 이를 반전한 위 조건식(!valueSet.add(...)
)은 true
가 됩니다.
즉, 중복된 값을 넣는 경우 에러를 throw
합니다. 이를 통해 중복 여부를 코드로 검증할 수 있습니다. 이 과정이 런타임에 생략되기를 원한다면 함수로 만들어 assert 함수() : "중복되는 값이 없어야 합니다.";
등으로 바꿀 수도 있죠.