Skip to main content

Command Palette

Search for a command to run...

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

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

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

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

More from this blog

클래스에 Serializable 인터페이스를 구현 받는 이유가 무엇인가요? #42

이 아티클은 깃허브 nettee-space 조직의 디스커션 #42 항목을 옮겨 온 것입니다.관련 논의: nettee-space/backend-sample-hexagonal-simple-crud/discussions/42 Question: 클래스에 Serializable 인터페이스를 구현 받는 이유가 무엇인가요? 여러 소스들을 접하면서 VO 객체 등에 Serializable를 구현받는 것을 많이 접했습니다. 저희 헥사고날(스터디 팀내 2단계 ...

Feb 7, 2025
클래스에 Serializable 인터페이스를 구현 받는 이유가 무엇인가요? #42

개념 2. JWT 액세스 토큰의 생성과 전달, Stateful한 리프레시 토큰의 생성, 전달, 보존

이전 글에서 정리에 꽤 힘을 뺐기 때문에, 이번 글에서는 서두와 부연설명을 줄이고 필요한 정보를 담아 전달해 보겠습니다. JWT 액세스 토큰 JWT 액세스 토큰은 인가에 직접 사용되는 토큰이고, stateless 하다는 장점이 있었습니다. 액세스 토큰의 생성 JWT(JSON Web Token)로 생성합니다. 비밀번호 인증 등 자격 검토 후 JWT를 발급합니다. JWT는 헤더, 페이로드, 시그니처 세 영역을 점(.) 기호로 구분한다고 했습니다....

Oct 23, 2024
개념 2. JWT 액세스 토큰의 생성과 전달, Stateful한 리프레시 토큰의 생성, 전달, 보존

Merge Simpson의 매너 있고 다정한 한국어 개발자 블로그

37 posts