KKanging

JAVA enum을 왜 사용하는 걸까? 본문

JAVA/언어

JAVA enum을 왜 사용하는 걸까?

천방지축 개발자 2024. 9. 30. 01:41

java 의 enum 이란

상수가 필요해!


public class EnumClient {
	private static final int ONE = 1;
	public static void main(String[] args) {
		System.out.println(ONE);
	}
}

우리는 프로그래밍 하는 도중에 상수가 필요하다면 위에 처럼

private static final 키워드를 사용해서 상수 선언을 한다.

추상화 된 상수가 필요해!


public class EnumClient {
	private static final int ONE = 1;
	private static final int TWO = 2;
	private static final int THREE = 3;
	public static void main(String[] args) {
		System.out.println(ONE);
		System.out.println(TWO);
		System.out.println(THREE);
	}
}

위처럼 상수의 목록이 추상화된 형태로 묶을 수 있고,

다른 client 코드에서 해당 상수를 사용해야한다면 일일해 상수 선언을 해야한다.

따라서 우리는 객체지향적 사고 방식으로 다음과 같이 선언한다.


class NumberConstClass {
	public static final int ONE = 1;
	public static final int TWO = 2;
	public static final int THREE = 3;
}

public class EnumClient {
	public static void main(String[] args) {
		System.out.println(NumberConstClass.ONE);
		System.out.println(NumberConstClass.TWO);
		System.out.println(NumberConstClass.THREE);
	}
}

위처럼 선언함으로써 다른 외부 클래스나 패키지에서 해당 클래스의 상수를 사용할 수 있게 되었고 상수 ONE TWO ,.. 들도 하나의 추상화된 단위로 묶일 수 있게 되었다.

해결한거 같지 않나?

클래스 상수 선언에 단점

  • 타입 세이프 하지 않다.
  • 상태(state) 와 로직이 분리되어 있다.
  • 논리적 동등성을 보장하지 않는다.

타입 세이프 하지 않다?


class NumberConstClass {
	public static final int ONE = 1;
	public static final int TWO = 2;
	public static final int THREE = 3;

	public static void outPut(int number1, int number2) {
		System.out.println(number1 + number2);
	}
}

public class EnumClient {
	public static void main(String[] args) {
		NumberConstClass.outPut(NumberConstClass.ONE, NumberConstClass.TWO);
	}
}

위에 방식으로 메서드를 출력하는 코드에서 인자로 NumberConstClass 를 넣지 않더라도 가능하다.


public class EnumClient {
	public static void main(String[] args) {
		NumberConstClass.outPut(NumberConstClass.ONE, 1231151515);
	}
}

이러한 타입 안정성이 없다는건 컴파일 단계에서 에러를 잡아낼 수 없기에 어떤 에러가 생길지 모른다.

상태와 로직이 분리?

상수를 모아둔 클래스이기 때문에 상태가 동작하는 메서드를 코드에 삽입할 수 없다.

논리적 동등성을 보장하지 않는다.


class NumberConstClass {
	public static final int ONE = 1;
	public static final int TWO = 2;
	public static final int THREE = 3;
}

class ShapeConstClass {
	public static final int CIRCLE = 1;
	public static final int SQUARE = 2;
	public static final int TRIANGLE = 3;
}
public class EnumClient {
	public static void main(String[] args) {
		System.out.println(NumberConstClass.ONE == ShapeConstClass.CIRCLE);
	}
}

논리적으로 위의 출력은 false 가 나와야 하지만 아쉽게도 true 가 나온다.

해결


class NumberConstClass {
	private final int value;

	public static final NumberConstClass ONE = new NumberConstClass(1);
	public static final NumberConstClass TWO = new NumberConstClass(2);
	public static final NumberConstClass THREE = new NumberConstClass(3);

	private NumberConstClass(int value) {
		this.value = value;
	}

	public void print() {
		System.out.println("Value: " + value);
	}
}

class ShapeConstClass {
	private final int value;

	public static final ShapeConstClass CIRCLE = new ShapeConstClass(1);
	public static final ShapeConstClass SQUARE = new ShapeConstClass(2);
	public static final ShapeConstClass TRIANGLE = new ShapeConstClass(3);

	private ShapeConstClass(int value) {
		this.value = value;
	}

	public void print() {
		System.out.println("Value: " + value);
	}
}
class Print{
	public static void Print(NumberConstClass numberConstClass) { // 타입 안정성 보장
		numberConstClass.print();
	}
}

public class EnumClient {
	public static void main(String[] args) {
		System.out.println(NumberConstClass.ONE == ShapeConstClass.CIRCLE); // 컴파일 에러
		NumberConstClass.ONE.print();
		ShapeConstClass.CIRCLE.print();
		Print.Print(NumberConstClass.ONE);
	}
}

싱글톤 디자인 기법을 활용해서 구현하였다.

위처럼 사용하면 우리가 원하는 상수를 추상화한 클래스를 잘 사용할 수 있을 것이다.

타입 안정성이 보장되고

로직이 들어가게 되고

논리적인 동등성을 보장할 수 있게 된다.

근데 너무 불편하다고 생각할 수 있다.

그래서 나온 enum 문법이 있다!

enum


public enum Number {
	ONE,TWO,THREE
}

enum 또한 위에 NumberConstClass 처럼 싱글톤으로 구현되어 있다.

처음 클래스가 로드될 때 상수 영역에 적재된다.

ONE,TWO,THREE 모두 하나의 싱글톤 객체이다.

enum도 클래스이고 값은 싱글톤 객체이다.


public enum Number {
	ONE(1){
		@Override
		void print() {
			System.out.println(ONE.value);
		}
	},
	TWO(2){
		@Override
		void print() {
			System.out.println(TWO.value);
		}
	},
	THREE(3){
		@Override
		void print() {
			System.out.println(THREE.value);
		}
	};

	private final int value;

	Number(int value) {
		this.value = value;
	}

	abstract void print();
}

public enum Number {
	ONE(value -> {
		System.out.println(value);
		return null;
	}),
	TWO(value -> {
		System.out.println(value);
		return null;
	}),
	THREE(value -> {
		System.out.println(value);
		return null;
	});

	private final Function<String,Void> function;

	Number(Function<String, Void> function) {
		this.function = function;
	}

	public void print(int value){
		this.function.apply(String.valueOf(value));
	};
}

어떤 상황에 enum을 사용해야 할까?

제일 중요한 enum의 사용 이유와 방법을 봐야한다.

위에서 상수 클래스 코드가 발전하는 걸 보면서 enum 이 만들어진 이유를 대충 확인해 볼 수 있었다.

이렇게 장황하게 설명한 이유는 enum 을 개발하면서 사용할까? 사용한다면 enum 밖에 방법이 없을까라는 생각을 했기 때문이다.

이번에 공부를 통해서 enum 의 존재 이유와 enum의 사용을 알아볼 수 있었다.

Spring 개발자가 enum을 사용하는 예제는 다음과 같다 (경험 했던 영역 내에서..)

데이터의 그룹화

상태와 행동을 한 곳에서 관리

논리적 동등성을 보장해야할 때 ( DB와 애플리케이션 간에, 서로 다른 데이터간에…)

다음은 현재 프로젝트에서 enum을 사용한 예이다.


@Getter
@RequiredArgsConstructor
public enum ExceptionCode {

	/* 401xx AUTH */
	INVALID_PHONE_NUMBER_OR_PASSWORD(HttpStatus.UNAUTHORIZED, 40101, "전화번호 또는 비밀번호가 일치하지 않습니다.",
		"사용자가 제공한 전화번호나 비밀번호가 데이터베이스의 정보와 일치하지 않을 때 발생합니다."),
	FORBIDDEN_ACCESS_TOKEN(HttpStatus.FORBIDDEN, 40102, "토큰에 접근 권한이 없습니다."),
	EMPTY_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, 40103, "토큰이 포함되어 있지 않습니다."),
	EXPIRED_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, 40104, "재 로그인이 필요합니다.",
		"해당 애러 발생시, RefreshToken을 통해 AccessToken을 재발급 해주세요. 해당 오류는 권한이 필요한 모든 엔드포인트에서 발생할 수 있습니다."),

	private final HttpStatus httpStatus;
	private final int errorCode;
	private final String message;
	private final String description;

	ExceptionCode(HttpStatus httpStatus, int errorCode, String message) {
		this(httpStatus, errorCode, message, "");
	}
}

다양한 enum 활용 예

Java Enum 활용기 | 우아한형제들 기술블로그 (woowahan.com)

요약

  • 데이터들 간에 논리적 동등성을 보장 해야할 때
  • 상태와 행위를 한 곳에서 관리 해야할 때
  • 데이터 그룹을 관리하기 위한 (데이터 끼리의 추상화)
  • DB와 객체간에 패러다임 추상화

public class DaysOfWeekBitmask {
	private static final byte MIN_VALID_BITMASK = 0x01; // 00000001
	private static final byte MAX_VALID_BITMASK = 0x7F; // 01111111

	private final byte bitmask;
	
	
	......
}

 

'JAVA > 언어' 카테고리의 다른 글

[JAVA] Reflection을 알아보자  (0) 2024.09.24
[JAVA] ThreadLocal  (0) 2024.05.18