1. 문자열과 타입 안정성1
package enumeration.ex0;
public class DiscountService {
public int discount(String grade, int price) {
int discountPercent = 0;
if (grade.equals("BASIC")) {
discountPercent = 10;
} else if (grade.equals("GOLD")) {
discountPercent = 20;
} else if (grade.equals("DIAMOND")) {
discountPercent = 30;
} else {
System.out.println(grade + "할인X");
return 0;
}
return price * discountPercent / 100;
}
}
package enumeration.ex0;
public class StringGradeEx0_1 {
public static void main(String[] args) {
int price = 10000;
DiscountService discountService = new DiscountService();
int basic = discountService.discount("BASIC", price);
int gold = discountService.discount("GOLD", price);
int diamond = discountService.discount("dIAMOND", price);
System.out.println("BASIC등급의 할인 금액: " + basic);
System.out.println("GOLD등급의 할인 금액: " + gold);
System.out.println("DIAMOND등급의 할인 금액: " + diamond);
}
}
실행결과
dIAMOND할인X
BASIC등급의 할인 금액: 1000
GOLD등급의 할인 금액: 2000
DIAMOND등급의 할인 금액: 0
String 사용 시 타입 안정성 부족 문제
- 값의 제한 부족: String 으로 상태나 카테고리를 표현하면, 잘못된 문자열을 실수로 입력할 가능성이 있다. 예를
들어, "Monday", "Tuesday" 등을 나타내는 데 String 을 사용한다면, 오타("Munday")나 잘못된 값
("Funday")이 입력될 위험이 있다. - 컴파일 시 오류 감지 불가: 이러한 잘못된 값은 컴파일 시에는 감지되지 않고, 런타임에서만 문제가 발견되기 때문
에 디버깅이 어려워질 수 있다.
2. 문자열과 타입 안정성2
package enumeration.ex1;
public class StringGrade {
public static final String BASIC = "BASIC";
public static final String GOLD = "GOLD";
public static final String DIAMOND = "DIAMOND";
}
package enumeration.ex1;
public class DiscountService {
public int discount(String grade, int price) {
int discountPercent = 0;
if (grade.equals(StringGrade.BASIC)) {
discountPercent = 10;
} else if (grade.equals(StringGrade.GOLD)) {
discountPercent = 20;
} else if (grade.equals(StringGrade.DIAMOND)) {
discountPercent = 30;
} else {
System.out.println(grade + "할인X");
return 0;
}
return price * discountPercent / 100;
}
}
package enumeration.ex1;
import enumeration.ex1.DiscountService;
public class StringGradeEx1_1 {
public static void main(String[] args) {
int price = 10000;
DiscountService discountService = new DiscountService();
int basic = discountService.discount(StringGrade.BASIC, price);
int gold = discountService.discount(StringGrade.GOLD, price);
int diamond = discountService.discount(StringGrade.DIAMOND, price);
System.out.println("BASIC등급의 할인 금액: " + basic);
System.out.println("GOLD등급의 할인 금액: " + gold);
System.out.println("DIAMOND등급의 할인 금액: " + diamond);
}
}
실행결과
BASIC등급의 할인 금액: 1000
GOLD등급의 할인 금액: 2000
DIAMOND등급의 할인 금액: 3000
- 첫번째로 구현한 코드와 달리 문자열 상수를 사용하여 전체적으로 코드가 명확해졌다.
- 하지만 문자열 상수를 사용해도, String 타입은 어떤 문자열이든 입력할 수 있기 때문에 근본적인 문제는 해결할 수 없다
int diamond = discountService.discount("DIAMOND", price);
- 다음과 같이 직접 문자열을 사용할 수 있기 때문에 근본적인 문제는 해결 x
3. 타입 안전 열거형 패턴
package enumeration.ex2;
public class ClassGrade {
public static final ClassGrade BASIC = new ClassGrade();
public static final ClassGrade GOLD = new ClassGrade();
public static final ClassGrade DIAMOND = new ClassGrade();
private ClassGrade() {}
}

- 각각을 상수로 선언하기 위해 static , final 을 사용한다.
- static 을 사용해서 상수를 메서드 영역에 선언한다.
- final 을 사용해서 인스턴스(참조값)를 변경할 수 없게 한다.
- 위 방식을 이용하면 값을 변경할 수 없기 때문에 위에서 다루었던 문제들을 해결할 수 있다.
- 또한 외부에서 임의로 ClassGrade의 인스턴스를 생성할 수 없도록 private 생성자를 만들어준다.
- private 생성자 덕분에 ClassGrade 의 인스턴스를 생성하는 것은 ClassGrade 클래스 내부에서만 할 수
있다. 앞서 우리가 정의한 상수들은 ClassGrade 클래스 내부에서 ClassGrade 객체를 생성한다.
이제 ClassGrade 인스턴스를 사용할 때는 ClassGrade 내부에 정의한 상수를 사용해야 한다. 그렇지 않으
면 컴파일 오류가 발생한다.
쉽게 이야기해서 ClassGrade 타입에 값을 전달할 때는 우리가 앞서 열거한 BASIC , GOLD , DIAMOND 상수
만 사용할 수 있다
제한된 인스턴스 생성: 클래스는 사전에 정의된 몇 개의 인스턴스만 생성하고, 외부에서는 이 인스턴스들만 사용
할 수 있도록 한다. 이를 통해 미리 정의된 값들만 사용하도록 보장한다.
타입 안전성: 이 패턴을 사용하면, 잘못된 값이 할당되거나 사용되는 것을 컴파일 시점에 방지할 수 있다. 예를 들
어, 특정 메서드가 특정 열거형 타입의 값을 요구한다면, 오직 그 타입의 인스턴스만 전달할 수 있다. 여기서는 메
서드의 매개변수로 ClassGrade 를 사용하는 경우, 앞서 열거한 BASIC , GOLD , DIAMOND 만 사용할 수 있다.
4. 열거형 Enum
**자바는 타입 안전 열거형 패턴"(Type-Safe Enum Pattern)을 매우 편리하게 사용할 수 있는 열거형(Enum Type)을
제공한다.
영어인 enum 은 enumeration 의 줄임말인데, 번역하면 열거라는 뜻이고, 어떤 항목을 나열하는 것을 뜻한다.
"Enumeration"은 일련의 명명된 상수들의 집합을 정의하는 것을 의미하며, 프로그래밍에서는 이러한 상수들을 사용하
여 코드 내에서 미리 정의된 값들의 집합을 나타낸다.
package enumeration.ex3;
public enum Grade {
BASIC,
GOLD,
DIAMOND
}
- Enum 클래스의 코드는 위에서 final static으로 정의한 클래스와 거의 비슷하다.
- 외부에서 임의로 생성 x

package enumeration.ex3;
public class EnumRefMain {
public static void main(String[] args) {
System.out.println("class BASIC = " + Grade.BASIC.getClass());
System.out.println("class BASIC = " + Grade.GOLD.getClass());
System.out.println("class BASIC = " + 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 BASIC = class enumeration.ex3.Grade
class BASIC = class enumeration.ex3.Grade
ref BASIC = 1b0375b3 // 참조값 -> 각각의 참조값은 다 다르다
ref GOLD = 2f7c7260
ref DIAMOND = 2d209079
- System.identityHashCode(grade) : 자바가 관리하는 객체의 참조값을 숫자로 반환한다.
- Integer.toHexString() : 숫자를 16진수로 변환, 우리가 일반적으로 확인하는 참조값은 16진수
** Enum을 활용한 예제코드
package enumeration.ex3;
public class DiscountService {
public int discount(Grade grade, int price) {
int discountPercent = 0;
if (grade == grade.BASIC) {
discountPercent = 10;
} else if (grade == grade.GOLD) {
discountPercent = 20;
} else if (grade == grade.DIAMOND) {
discountPercent = 30;
} else {
System.out.println(grade + "할인X");
}
return price * discountPercent / 100;
}
}
package enumeration.ex3;
import enumeration.ex2.ClassGrade;
public class ClassGradeEx3_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);
}
}
- 열거형(ENUM)의 장점
- 타입 안정성 향상: 열거형은 사전에 정의된 상수들로만 구성되므로, 유효하지 않은 값이 입력될 가능성이 없다. 이
런 경우 컴파일 오류가 발생한다. - 간결성 및 일관성: 열거형을 사용하면 코드가 더 간결하고 명확해지며, 데이터의 일관성이 보장된다.
확장성: 새로운 회원 등급을 타입을 추가하고 싶을 때, ENUM에 새로운 상수를 추가하기만 하면 된다.
- 타입 안정성 향상: 열거형은 사전에 정의된 상수들로만 구성되므로, 유효하지 않은 값이 입력될 가능성이 없다. 이
5. 열거형 주요 메서드
package enumeration.ex3;
import java.util.Arrays;
public class EnumMethodMain {
// 모든 Enum 반환
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 변환
String input = "GOLD";
Grade gold = Grade.valueOf(input);
System.out.println("gold = " + gold);
}
}
실행결과
values = [BASIC, GOLD, DIAMOND]
name=BASIC, ordinal=0
name=GOLD, ordinal=1
name=DIAMOND, ordinal=2
gold = GOLD
- ENUM - 주요 메서드
- values(): 모든 ENUM 상수를 포함하는 배열을 반환한다.
- valueOf(String name): 주어진 이름과 일치하는 ENUM 상수를 반환한다.
- name(): ENUM 상수의 이름을 문자열로 반환한다.
- ordinal(): ENUM 상수의 선언 순서(0부터 시작)를 반환한다.
- toString(): ENUM 상수의 이름을 문자열로 반환한다. name() 메서드와 유사하지만, toString() 은 직접
오버라이드 할 수 있다.
- 주의 ordinal()은 가급적 사용하지 않는 것이 좋다.
6. 열거형 리펙토링
package enumeration.ref3.ref2;
public enum Grade {
BASIC(10),
GOLD(20),
DIAMOND(30),
VIP(40);
private final int discountPercent;
Grade(int discountPercent) {
this.discountPercent = discountPercent;
}
public int getDiscountPrice(int price) {
return price * discountPercent / 100;
}
}
- discountPercent 필드를 추가하고, 생성자를 통해서 필드에 값을 저장한다.
- 열거형은 상수로 지정하는 것 외에 일반적인 방법으로 생성이 불가능하다. 따라서 생성자에 접근제어자를 선언할
수 없게 막혀있다. private 이라고 생각하면 된다. - BASIC(10) 과 같이 상수 마지막에 괄호를 열고 생성자에 맞는 인수를 전달하면 적절한 생성자가 호출된다.
값을 조회하기 위해 getDiscountPercent() 메서드를 추가했다. 열거형도 클래스이므로 메서드를 추가할
수 있다.
package enumeration.ref3.ref2;
public class ClassGradeRefMain {
public static void main(String[] args) {
int price = 10000;
//
// printDiscount(Grade.BASIC, price);
// printDiscount(Grade.GOLD, price);
// printDiscount(Grade.DIAMOND, price);
Grade[] grades = Grade.values();
for (Grade grade : grades) {
printDiscount(grade, price);
}
}
private static void printDiscount(Grade grade, int price) {
System.out.println(grade.name() + "등급의 할인 금액: " + grade.getDiscountPrice(price));
}
}
실행 결과
BASIC 등급의 할인 금액: 1000
GOLD 등급의 할인 금액: 2000
DIAMOND 등급의 할인 금액: 3000
'Java' 카테고리의 다른 글
| Java 중급 1 - 날짜와 시간(2) (0) | 2025.03.19 |
|---|---|
| Java 중급 1 - 날짜와 시간(1) (0) | 2025.03.16 |
| Java 중급 1 - Wrapper Class (0) | 2025.02.24 |
| Java 중급 1 - String (0) | 2025.02.14 |
| Java 중급 1 - 불변객체 (0) | 2025.02.14 |