# Enum아 Enum아, 너만 말고 너의 값도 유니크해 다오 Java

> 🇰🇷 Kor: 지금 보는 중!  
> 🗺 Eng: Not yet!  
> 🇯🇵 Jap: まだです。

---

# 🪖 "enum은 개인주의야."

자바에서 `enum`은 열거 타입이라고 해서, 여러 선택지를 나열한 특별한 클래스 유형이죠. 그래서 기본적으로 서로 구분되는 유니크 상태입니다. 서로 구분되는 상태나 역할 등을 표현하는 데에 좋습니다.

```java
public enum AccountStatus {
    PENDING,
    ACTIVE,
    PROTECTED,
    SUSPENDED,
    SLEPT,
    REMOVED
}
```

약간 더 구체적인 작성 양식은 이렇죠. 아마 이 글을 보시는 분들은 보통 아실 것 같습니다!

```java
public enum 클래스_식별자 { // PascalCase
    열거상수(생성자가 없다면 소괄호 생략), // UPPER_CASE
    열거상수(생성자가 없다면 소괄호 생략),
    ...
    세미콜론이 나올 때까지 콤마로 구분하여 열거상수 나열
    ... ;
    
    // 세미콜론 이후로 일반 클래스처럼 필드 등을 작성합니다.

    // 생성자는 외부에서 사용할 수 없습니다.
}
```

필드를 준 예시는 이렇겠죠. (`@RequiredArgsConstructor`는 생성자를 만들어 주는 롬복 애노테이션)

```java
@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 블록보다 장유유서합니다.

자바의 클래스에는 클래스 로드 타임에 동작하는 정적 블록이 있습니다.

```java
// 이렇게 생겼고요, 아는 분들은 다 아는데 모르는 분들은 말 안 해주면 모르는 그런 겁니다.
static {
    // 여기에 실행문 쓰면 클래스가 처음 쓰일 때 딱 한 번 실행됩니다.
    System.out.println("클래스가 메모리에 올라갈 때 실행되는 공간이오.");
}
```

클래스 로드 타임에 동작하니까, 동작 시점은 이렇게 되는 게 당연했어요:

```plaintext
클래스 "에헴, 메모리에 오르겠노라."
정적 블록 "(나는 이때 수행되어야 하는, 클래스 소유의 실행문이오.) 두둥!"
```

`enum`도 클래스니까 정적 블록이 있는데, `enum` 클래스는 나열된 상수가 없으면 완성될 수 없기 때문에, 열거 상수를 만드는 각 **<mark>'생성자'가 'static statements block'보다 먼저</mark>** 실행됩니다.

```plaintext
클래스(enum) "에헴, 메모리에 오르겠노라."
열거상수들 "나도 나도 나 돋 나도나도도도 나도. 우리 없이 너네가 뭐 돼?" (생성자)
정적 블록 "선배님들 먼저 오르시지 말입니다..."
```

그래서 정적 블록이 실행될 때 이미 열거 상수는 생성되어 있어요. 물론 필드도요. 그래서 정적 블록에서 필드를 확인할 수 있다는 특징이 있습니다.

**필드에 대한 필터는** `static` **블록에서 작성하면 됩니다**:

```java
static {
    for (var item : values()) {
        // 각 열거 항목(열거 상수)의 필드를 확인할 수 있습니다.
        System.out.println("선배님들 딱 대시지 말입니다: " + item.field);
    }
}
```

이제 유니크 필터를 간단히 씌워 봅시다.

---

# Enum의 하위 필드의 유니크 필터

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1700633187770/b84e6844-01a9-4b79-883e-ed18aa7e9ce9.png align="center")

`static` 블럭을 사용한다는 점이 특징일 뿐, 그 내부에서 구현은 거창한 것 없이 그냥 유니크하게 관리하면 됩니다. 간단하게는 `Set` 타입을 사용해 중복을 체크할 수 있겠죠.

자바가 원래 클래스로드 타임은 동시성을 보장하니까 `Concurrent` 계열(ex: `ConcurrentSkipListSet<...>`)을 안 써도 됩니다.

```java
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 함수() : "중복되는 값이 없어야 합니다.";` 등으로 바꿀 수도 있죠.
