자바의 열거형 특징 및 사용법

자바의 열거형 특징 및 사용법

반응형

목표

자바의 열거형에 대해 학습하세요.

학습할 것 (필수)

enum 정의하는 방법

enum이 제공하는 메소드 (values()와 valueOf())

java.lang.Enum

EnumSet

추천 도서

www.yes24.com/Product/Goods/7516911

www.acornpub.co.kr/book/jpa-programmig

Enum

enum은 열거형입니다. jdk-1.5부터 도입된 개념입니다.

먼저 Enum이 없었다면 어떻게 상수를 선언하는지 살펴보겠습니다.

public static void main(String[] args) { switch_test("1"); } private static void demoTest(String param) { if (param.equals("1")) { System.out.println("print 1"); } else if (param.equals("2")) { System.out.println("print 2"); } else { System.out.println("print else"); } }

위 코드는 메서드에서 넘어오는 파라미터의 값으로 분기 처리하는 코드입니다.

이 코드는 type-safety하지 않습니다. 이렇게 하드코딩하여 작성하게되면 개발자의 실수로 오타가 발생하면 예상하는 다른 결과를 발생시킵니다. 그렇다면 상수 처리를 해보겠습니다.

public class Store { public static final String ONE = "1"; public static final String TWO = "2"; public static void main(String[] args) { switch_test("1"); } private static void demoTest(String param) { if (param.equals(ONE)) { System.out.println("print 1"); } else if (param.equals(TWO)) { System.out.println("print 2"); } else { System.out.println("print else"); } } }

상수를 사용하여 type-safety한 코드를 작성하였습니다. 하지만 프로젝트가 커지면서 상수값도 많아질 것이고 동일한 값을 가지는 상수들도 생기게 될 것입니다. 다시 코드를 살펴보겠습니다.

public class Store { public static final String ONE = "1"; public static final String TWO = "2"; public static final String SEQUENCE_FIRST = "1"; public static final String SEQUENCE_SECOND = "2"; public static void main(String[] args) { switch_test("1"); } private static void demoTest(String param) { if (param.equals(ONE)) { System.out.println("print 1"); } else if (param.equals(SEQUENCE_SECOND)) { System.out.println("print 2"); } else { System.out.println("print else"); } } }

내가 의도한 상태는 ONE 상수와 TWO 상수로 조건 분기를 태우고 싶었습니다. 하지만 SECOND를 실수로 넣는 코드를 작성하였습니다. 하지만 이 코드는 정상적으로 작동할 것입니다. 그리고 다음 개발자가 이 소스를 본다면 혼동이 올 것입니다. 상수를 그룹화시켜보겠습니다.

public class StoreNumberConstant { public static final String ONE = "1"; public static final String TWO = "2"; }

public class StoreSequenceConstant { public static final String FIRST = "1"; public static final String SECOND = "2"; }

public class Store { public static void main(String[] args) { switch_test("1"); } private static void demoTest(String param) { if (param.equals(StoreNumberConstant.ONE)) { System.out.println("print 1"); } else if (param.equals(StoreNumberConstant.TWO)) { System.out.println("print 2"); } else { System.out.println("print else"); } } }

여기까지 왔으면 뭔가 그럴싸하게 완성된 것 같습니다. 이정도만 되도 될 것 같은데? 라고 생각할 수도 있지만 다음 코드를 살펴보겠습니다.

public class StaffAgeConstant { public static int ONE = 1; public static int TWO = 2; }

public class StaffSequenceConstant { public static int ONE = 1; public static int TWO = 2; }

public class Staff { public static void main(String[] args) { if (StaffAgeConstant.ONE == StaffSequenceConstant.ONE) { System.out.println("실행되면 안된다."); } } }

전혀 상관없는 상수끼리 비교했을 때에도 true처리되어 print를 실행하는 코드입니다. 이러한 다양한 문제점들을 보완하기 위해 Enum을 사용합니다.

public enum Age { ONE, TWO }

public enum Number { ONE, TWO }

public class Animals { public static void main(String[] args) { if (Age.ONE == Number.ONE) { // 컴파일 에러 발생 System.out.println("컴파일 에러난다~"); } } }

위처럼 Enum은 Class에서 상수처리한 것과 다르게 사용할 수 있습니다.

enum 정의하는 방법

public enum Age { ONE, TWO }

class대신 enum을 선언하여 사용하고 상수를 선언하여 사용할 수 있습니다.

public enum Age { ONE(1), TWO(2); private int age; Age(int age) { this.age = age; } public int getAge() { return this.age; } }

이런식으로도 상수에 괄호를 사용하면 생성자를 생성하여 변수를 추가할 수 있습니다. 그리고 이번에 학습하면서 Enum도 추상화 메서드를 선언하여 사용할 수 있다는 것을 알았습니다.

public enum Age { ONE(1, 2) { @Override int calculateAge(int index) { return age * index; } } , TWO(2, 3) { @Override int calculateAge(int index) { return age * index; } }; protected final int age; protected final int CALCULATE_AGE; Age(int age, int calculateAge) { this.age = age; this.CALCULATE_AGE = calculateAge; } public int getAge() { return this.age; } abstract int calculateAge(int index); // age에 곱하기 해보자 }

public static void main(String[] args) { System.out.println(Age.ONE.calculateAge(2)); System.out.println(Age.ONE.calculateAge(3)); System.out.println(Age.TWO.calculateAge(2)); System.out.println(Age.TWO.calculateAge(3)); } // 결과값은 2, 3, 4, 6

enum이 제공하는 메소드 (values()와 valueOf())

먼저 values()와 valueOf()를 비교하기 전에 Enum 을 생성하면 Enum Class를 상속받는 것을 알아야합니다. enum을 생성하고 바이트코드로 알아보겠습니다.

public enum MoveEnum { TEST1, TEST2 }

// class version 52.0 (52) // access flags 0x4031 // signature Ljava/lang/Enum; // declaration: com/example/practice/moves/MoveEnum extends java.lang.Enum public final enum com/example/practice/moves/MoveEnum extends java/lang/Enum { // compiled from: MoveEnum.java // access flags 0x4019 public final static enum Lcom/example/practice/moves/MoveEnum; TEST1 // access flags 0x4019 public final static enum Lcom/example/practice/moves/MoveEnum; TEST2 // access flags 0x101A private final static synthetic [Lcom/example/practice/moves/MoveEnum; $VALUES // access flags 0x9 public static values()[Lcom/example/practice/moves/MoveEnum; L0 LINENUMBER 3 L0 GETSTATIC com/example/practice/moves/MoveEnum.$VALUES : [Lcom/example/practice/moves/MoveEnum; INVOKEVIRTUAL [Lcom/example/practice/moves/MoveEnum;.clone ()Ljava/lang/Object; CHECKCAST [Lcom/example/practice/moves/MoveEnum; ARETURN MAXSTACK = 1 MAXLOCALS = 0 // access flags 0x9 public static valueOf(Ljava/lang/String;)Lcom/example/practice/moves/MoveEnum; // parameter mandated name L0 LINENUMBER 3 L0 LDC Lcom/example/practice/moves/MoveEnum;.class ALOAD 0 INVOKESTATIC java/lang/Enum.valueOf (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; CHECKCAST com/example/practice/moves/MoveEnum ARETURN L1 LOCALVARIABLE name Ljava/lang/String; L0 L1 0 MAXSTACK = 2 MAXLOCALS = 1 // access flags 0x2 // signature ()V // declaration: void () private (Ljava/lang/String;I)V // parameter synthetic $enum$name // parameter synthetic $enum$ordinal L0 LINENUMBER 3 L0 ALOAD 0 ALOAD 1 ILOAD 2 INVOKESPECIAL java/lang/Enum. (Ljava/lang/String;I)V RETURN L1 LOCALVARIABLE this Lcom/example/practice/moves/MoveEnum; L0 L1 0 MAXSTACK = 3 MAXLOCALS = 3 // access flags 0x8 static ()V L0 LINENUMBER 4 L0 NEW com/example/practice/moves/MoveEnum DUP LDC "TEST1" ICONST_0 INVOKESPECIAL com/example/practice/moves/MoveEnum. (Ljava/lang/String;I)V PUTSTATIC com/example/practice/moves/MoveEnum.TEST1 : Lcom/example/practice/moves/MoveEnum; NEW com/example/practice/moves/MoveEnum DUP LDC "TEST2" ICONST_1 INVOKESPECIAL com/example/practice/moves/MoveEnum. (Ljava/lang/String;I)V PUTSTATIC com/example/practice/moves/MoveEnum.TEST2 : Lcom/example/practice/moves/MoveEnum; L1 LINENUMBER 3 L1 ICONST_2 ANEWARRAY com/example/practice/moves/MoveEnum DUP ICONST_0 GETSTATIC com/example/practice/moves/MoveEnum.TEST1 : Lcom/example/practice/moves/MoveEnum; AASTORE DUP ICONST_1 GETSTATIC com/example/practice/moves/MoveEnum.TEST2 : Lcom/example/practice/moves/MoveEnum; AASTORE PUTSTATIC com/example/practice/moves/MoveEnum.$VALUES : [Lcom/example/practice/moves/MoveEnum; RETURN MAXSTACK = 4 MAXLOCALS = 0 }

바이트 코드를 살펴보면 Enum Class를 상속받는 것을 확인할 수 있고 valueOf 메서드가 생성된 것을 확인할 수 있습니다. 결론을 내리면 아래와 같습니다.

valueOf() Enum Class 내에 존재 parameter로 넘기는 Enum 상수를 리턴합니다.

values() 컴파일러가 추가 Enum 내 모든 상수를 리턴합니다.

public class Move { public static void main(String[] args) { MoveEnum[] values = MoveEnum.values(); for (MoveEnum value : values) { System.out.println(value); } MoveEnum test1 = MoveEnum.valueOf("TEST1"); System.out.println(test1); } } // results // TEST1 // TEST2 // TEST1

java.lang.enum

앞 단락에서 설명했듯이 enum은 java.lang.enum 클래스를 상속받고 있습니다. 그럼 어떤 메서드가 있는지 살펴보겠습니다.

name ()

public static void main(String[] args) { System.out.println(MoveEnum.TEST1.name()); }

TEST1

상수를 String 타입으로 리턴합니다.

ordinal ()

public static void main(String[] args) { System.out.println(MoveEnum.TEST1.ordinal()); System.out.println(MoveEnum.TEST2.ordinal()); }

0 1

Enum에서 선언한 상수값들의 순서롤 리턴합니다. ordinal() 메서드를 사용할 때는 주의할 점이 있습니다. Enum의 순서로 조건을 태워 로직을 실행하는 코드는 위험 가능성이 있습니다.

public static void main(String[] args) { if ( MoveEnum.TEST1.ordinal() == 0) { System.out.println("0"); } }

이런 코드는 다른 개발자가 Enum 클래스에 상수를 추가할 때 ordinal을 고려하지 못하고 순서를 뒤죽박죽으로 바꿔서 추가할 수 있습니다. 그럼 이 코드는 생각했던 방향과는 다른 방향으로 로직을 실행할 것 입니다.

values(), valueOf() 메서드도 있지만 위에서 언급하여 생각하겠습니다.

EnumSet

Enum을 Set 자료구조형으로 만들 수 있습니다.

allOf() : Enum Class를 추가하면 선언된 모든 상수를 Set 자료구조형으로 변환

of() : Enum Class에서 추가하고 싶은 항목만 선언하면 선언된 값들로 Set 자료구조형으로 변환

noneOf : 아무것도 추가하지 않음

public static void main(String[] args) { EnumSet moveEnumsAll = EnumSet.allOf(MoveEnum.class); for (MoveEnum moveEnum : moveEnumsAll) { System.out.println("allOf : " + moveEnum.name()); } EnumSet moveEnums = EnumSet.of(MoveEnum.TEST1, MoveEnum.TEST2); for (MoveEnum moveEnum : moveEnums) { System.out.println("of : " + moveEnum.name()); } EnumSet moveEnumsNone = EnumSet.noneOf(MoveEnum.class); for (MoveEnum moveEnum : moveEnumsNone) { System.out.println("noneOf : " + moveEnum.name()); } }

allOf : TEST1 allOf : TEST2 of : TEST1 of : TEST2

반응형

from http://hadesyi.tistory.com/212 by ccl(A) rewrite - 2021-12-29 03:27:43