오늘의 코딩
오늘의 공부 이따간 또 합주하러 가야한다. 합주 하기 전 짬내서 코딩
자바는 타입 안전 열거형 패턴"(Type-Safe Enum Pattern)을 매우 편리하게 사용할 수 있는 열거형(Enum Type)을
제공한다.
쉽게 이야기해서 자바의 열거형은 앞서 배운 타입 안전 열거형 패턴을 쉽게 사용할 수 있도록 프로그래밍 언어에서 지원
하는 것이다.
영어인 enum 은 enumeration 의 줄임말인데, 번역하면 열거라는 뜻이고, 어떤 항목을 나열하는 것을 뜻한다.
"Enumeration"은 일련의 명명된 상수들의 집합을 정의하는 것을 의미하며, 프로그래밍에서는 이러한 상수들을 사용하
여 코드 내에서 미리 정의된 값들의 집합을 나타낸다.
쉽게 이야기해서 회원의 등급은 상수로 정의한 BASIC , GOLD , DIAMOND 만 사용할 수 있다는 뜻이다.
자바의 enum은 타입 안전성을 제공하고, 코드의 가독성을 높이며, 예상 가능한 값들의 집합을 표현하는 데 사용된다.
package enumeration.ex3;
public enum Grade {
BASIC, GOLD, DIAMOND
}
class 대신에 enum 을 사용한다.앞서 직접 ClassGrade 를 구현할 때와는 비교가 되지 않을 정도로 편리하다.
자바의 열거형으로 작성한 Grade 는 다음 코드와 거의 같다.
public class Grade extends Enum {
public static final Grade BASIC = new Grade();
public static final Grade GOLD = new Grade();
public static final Grade DIAMOND = new Grade();
//private 생성자 추가
private Grade() {}
}
java.lang.Enum 을 상속 받는다.
열거형을 코드로 확인해보자.
package enumeration.ex3;
public class EnumRefMain {
public static void main(String[] args) {
System.out.println("class BASIC = " + Grade.BASIC.getClass());
System.out.println("class GOLD = " + Grade.GOLD.getClass());
System.out.println("class DIAMOND = " + Grade.DIAMOND.getClass());
System.out.println("ref BASIC = " + refValue(Grade.BASIC));
System.out.println("ref GOLD = " + refValue(Grade.GOLD));
System.out.println("ref DIAMOND = " + refValue(Grade.DIAMOND));
}
private static String refValue(Object grade) {
return Integer.toHexString(System.identityHashCode(grade));
}
}
실행 결과
class BASIC = class enumeration.ex3.Grade
class GOLD = class enumeration.ex3.Grade
class DIAMOND = class enumeration.ex3.Grade
ref BASIC = x001
ref GOLD = x002
ref DIAMOND = x003
Grade 타입을 사용하는 것을 확인할 수 있다. 그리고 각각의 인스턴스도 서로 다른 것을 확인할 수 있다.toString() 을 재정의 하기 때문에 참조값을 직접 확인할 수 없다. 참조값을 구하기 위해refValue() 를 만들었다.System.identityHashCode(grade) : 자바가 관리하는 객체의 참조값을 숫자로 반환한다.Integer.toHexString() : 숫자를 16진수로 변환, 우리가 일반적으로 확인하는 참조값은 16진수자바의 열거형을 사용해서 코드를 작성해보자.
package enumeration.ex3;
public class DiscountService {
public int discount(Grade grade, int price) {
int discountPercent = 0;
//enum switch 변경 가능
if (grade == Grade.BASIC) {
discountPercent = 10;
} else if (grade == Grade.GOLD) {
discountPercent = 20;
} else if (grade == Grade.DIAMOND) {
discountPercent = 30;
} else {
System.out.println("할인X");
}
return price * discountPercent / 100;
}}
package enumeration.ex3;
public class EnumEx3_1 {
public static void main(String[] args) {
int price = 10000;
DiscountService discountService = new DiscountService();
int basic = discountService.discount(Grade.BASIC, price);
int gold = discountService.discount(Grade.GOLD, price);
int diamond = discountService.discount(Grade.DIAMOND, price);
System.out.println("BASIC 등급의 할인 금액: " + basic);
System.out.println("GOLD 등급의 할인 금액: " + gold);
System.out.println("DIAMOND 등급의 할인 금액: " + diamond);
}
}
실행 결과
BASIC 등급의 할인 금액: 1000
GOLD 등급의 할인 금액: 2000
DIAMOND 등급의 할인 금액: 3000
switch 문에 사용할 수 있는 장점도 있다.열거형은 외부 생성 불가
package enumeration.ex3;
public class EnumEx3_2 {
public static void main(String[] args) {
int price = 10000;
DiscountService discountService = new DiscountService();
/* Grade myGrade = new Grade(); //enum 생성 불가
double result = discountService.discount(myGrade, price);
System.out.println("result price: " + result);
*/
}
}
enum 은 열거형 내부에서 상수로 지정하는 것 외에 직접 생성이 불가능하다. 생성할 경우 컴파일 오류가 발생한다.enum classes may not be instantiated열거형(ENUM)의 장점
참고
열거형을 사용하는 경우 static import 를 적절하게 사용하면 더 읽기 좋은 코드를 만들 수 있다.
모든 열거형은 java.lang.Enum 클래스를 자동으로 상속 받는다. 따라서 해당 클래스가 제공하는 기능들을 사용할 수 있다.
package enumeration.ex3;
import java.util.Arrays;
public class EnumMethodMain {
public static void main(String[] args) {
//모든 ENUM 반환
Grade[] values = Grade.values(); System.out.println("values = " + Arrays.toString(values));
for (Grade value : values) {
System.out.println("name=" + value.name() + ", ordinal=" +
value.ordinal());
}
//String -> ENUM 변환, 잘못된 문자면 IllegalArgumentException 발생
String input = "GOLD";
Grade gold = Grade.valueOf(input);
System.out.println("gold = " + gold); //toString() 오버라이딩 가능
}
}
실행 결과
values = [BASIC, GOLD, DIAMOND]
name=BASIC, ordinal=0
name=GOLD, ordinal=1
name=DIAMOND, ordinal=2
gold = GOLD
Arrays.toString() 배열의 참조값이 아니라 배열 내부의 값을 출력할 때 사용한다.
ENUM - 주요 메서드
name() 메서드와 유사하지만, toString() 은 직접 오버라이드 할 수 있다.주의 ordinal()은 가급적 사용하지 않는 것이 좋다.
ordinal() 의 값은 가급적 사용하지 않는 것이 좋다. 왜냐하면 이 값을 사용하다가 중간에 상수를 선언하는 위BASIC 다음에 SILVER 등급이 추가되는 경우 GOLD , DIAMOND 의 값이 하나씩 추가된다.기존
BASIC: 0GOLD: 1DIAMOND: 2BASIC: 0SILVER: 1GOLD: 2DIAMOND: 3기존 GOLD 의 ordinal() 값인 1 을 데이터베이스나 파일에 저장하고 있었는데, 중간에 SILVER 가 추가되면 데이터베이스나 파일에 있는 값은 그대로 1로 유지되지만, 애플리케이션 상에서 GOLD 는 2가 되고, SILVER 는 1이 된다.
쉽게 이야기해서 ordinal() 의 값을 사용하면 기존 GOLD 회원이 갑자기 SILVER 가 되는 큰 버그가 발생할 수 있다.
열거형 정리
java.lang.Enum 를 자동(강제)으로 상속 받는다.java.lang.Enum 을 상속 받았기 때문에 추가로 다른 클래스를 상속을 받을 수 없다.이번 시간에는 지금까지 구현한 코드들을 더 읽기 쉽게 리팩토링해보자.
아직 열거형(ENUM)에 익숙하지 않으니, 앞서 클래스를 직접 사용해서 열거형 패턴을 구현했던 ex2 의 코드를 먼저
리팩토링해보자.
DiscountService.discount() 코드를 살펴보자.
if (classGrade == ClassGrade.BASIC) {
discountPercent = 10;
} else if (classGrade == ClassGrade.GOLD) {
discountPercent = 20;
} else if (classGrade == ClassGrade.DIAMOND) {
discountPercent = 30;
} else { System.out.println("할인X");
}
if 문을 제거하자.discountPercent )은 각각의 회원 등급별로 판단된다. 할인율은 결국 회원 등급을 따라discountPercent )을 가지고 관리하도록 변경하자.package enumeration.ref1;
public class ClassGrade {
public static final ClassGrade BASIC = new ClassGrade(10);
public static final ClassGrade GOLD = new ClassGrade(20);
public static final ClassGrade DIAMOND = new ClassGrade(30);
private final int discountPercent;
private ClassGrade(int discountPercent) {
this.discountPercent = discountPercent;
}
public int getDiscountPercent() {
return discountPercent;
}
}
ClassGrade 에 할인율( discountPercent ) 필드를 추가했다. 조회 메서드도 추가한다.discountPercent 를 설정하도록 했고, 중간에 이 값이 변하지 않도록 불변으로 설계했다.discountPercent )이 정해진다.package enumeration.ref1;
public class DiscountService {
public int discount(ClassGrade classGrade, int price) {
return price * classGrade.getDiscountPercent() / 100;
}
}
if 문이 완전히 제거되고, 단순한 할인율 계산 로직만 남았다.if 문을 통해서 회원의 등급을 찾고, 각 등급 별로 discountPercent 의 값을 지정했다.if 문을 사용할 이유가 없다. 단순히 회원 등급안에 있는 getDiscountPercent() 메서드를 호출하면 인수로 넘어온 회원 등급의 할인율을 바로 구할 수 있다.package enumeration.ref1;
public class ClassGradeRefMain1 {
public static void main(String[] args) {
int price = 10000;
DiscountService discountService = new DiscountService();
int basic = discountService.discount(ClassGrade.BASIC, price);
int gold = discountService.discount(ClassGrade.GOLD, price);
int diamond = discountService.discount(ClassGrade.DIAMOND, price);
System.out.println("BASIC 등급의 할인 금액: " + basic);
System.out.println("GOLD 등급의 할인 금액: " + gold);
System.out.println("DIAMOND 등급의 할인 금액: " + diamond);
}
}
실행 결과
BASIC 등급의 할인 금액: 1000
GOLD 등급의 할인 금액: 2000
DIAMOND 등급의 할인 금액: 3000
실행 결과는 기존 코드와 같다.