본 글은 김영한 님의 『김영한의 실전 자바 - 중급 1편』 강의를 학습하며 정리한 내용입니다.
강의 자료에 포함된 일부 코드와 이미지를 참고하여 발췌·활용하였습니다.자바 기본기를 제대로 다지고 싶으시다면, 아래 링크에서 강의를 확인해 보세요
『김영한의 실전 자바 - 중급 1편』 보러 가기
본게시물은 파트너스 활동의 일환으로 작성되었으며, 구매 시 소정의 수수료를 받을 수 있습니다.
김영한의 실전 자바 - 중급 1편| 김영한 - 인프런 강의
현재 평점 5.0점 수강생 10,698명인 강의를 만나보세요. 실무에 필요한 자바의 다양한 중급 기능을 예제 코드로 깊이있게 학습합니다. 실무에 필요한 다양한 자바 중급 기능, Object, 불변 객체, String
www.inflearn.com
문자열과 타입 안전성
String 사용 시 타입 안정성 부족 문제
- 값의 제한 부족
- String 으로 상태나 카테고리를 표현하면, 잘못된 문자열을 실수로 입력할 가능성이 있다.
- 예를 들어, Monday,Tuesday 등을 나타내는데 String을 사용한다면 오타나 잘못된 값이 입력될 위험이 있다.
- 컴파일 시 오류 감지 불가
- 잘못된 값은 컴파일 시에는 감지되지 않고, 런타임에서만 문제가 발견되기 때문에 디버깅이 어려워질 수 있다.
이러한 문제들을 해결하려면 특정 범위로 값을 제한해야 한다.
타입 안전 열거형 패턴
타입 안전 열거형 패턴 - Type-Safe Enum Pattern
위 문제를 해결하기 위해 나온 결과가 바로 타입 안전 열거형 패턴이다. 여기서 enum 은 enumeration 의 줄임말로, 열거라는 뜻이고, 어떤 항목을 나열하는 것을 뜻한다.
여기서 중요한 것은 타입 안전 열거형 패턴을 사용하면 나열한 항목만 사용할 수 있다는 것이 핵심이다.
한번 코드로 구현해보자.
public class ClassGrade {
public static final ClassGrade BASIC = new ClassGrade();
public static final ClassGrade GOLD = new ClassGrade();
public static final ClassGrade DIAMOND = new ClassGrade();
}
- 회원 등급을 다루는 클래스를 만들고, 각각의 회원 등급별로 상수를 선언한다.
- 이 때 각각의 상수마다 별도의 인스턴스를 생성하고, 생성한 인스턴스를 대입한다.
- 상수로 선언하기 위해 static, final 을 사용한다.
- static 을 사용해 상수를 메서드 영역에 선언한다.
- final 을 사용해 인스턴스(참조값)를 변경할 수 없게 한다.
public class ClassRefMain {
public static void main(String[] args) {
System.out.println("class BASIC = " + ClassGrade.BASIC.getClass());
System.out.println("class GOLD = " + ClassGrade.GOLD.getClass());
System.out.println("class DIAMOND = " +ClassGrade.DIAMOND.getClass());
System.out.println("ref BASIC = " + ClassGrade.BASIC);
System.out.println("ref GOLD = " + ClassGrade.GOLD);
System.out.println("ref DIAMOND = " + ClassGrade.DIAMOND);
}
}
--실행 결과--
class BASIC = class enumeration.ex2.ClassGrade
class GOLD = class enumeration.ex2.ClassGrade
class DIAMOND = class enumeration.ex2.ClassGrade
ref BASIC = enumeration.ex2.ClassGrade@x001
ref GOLD = enumeration.ex2.ClassGrade@x002
ref DIAMOND = enumeration.ex2.ClassGrade@x003
- 각각의 상수는 모두 ClassGrade 타입을 기반으로 인스턴스를 만들었기 때문에 getClass() 의 결과는 모두 ClassGrade 이다.
- 각각의 상수는 모두 서로 다른 ClassGrade 인스턴스를 참조하기 때문에 참조값이 다르게 출력된다.
- static 이므로 애플리케이션 로딩 시점에 다음과 같이 3개의 ClassGrade 인스턴스가 생성되고, 각각의 상수는 같은 ClassGrade 타입의 서로 다른 인스턴스 참조값을 가진다.
public class DiscountService {
public int discount(ClassGrade classGrade, int price) {
int discountPercent = 0;
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");
}
return price * discountPercent / 100;
}
}
- discount() 메서드는 매개변수로 ClassGrade 클래스를 사용한다.
- 값을 비교할 때는 classGrade == ClassGrade.BASIC 와 같이 == 참조값 비교를 사용하면 된다.
- 매개변수에 넘어오는 인수도 ClassGrade 가 가진 상수 중에 하나를 사용한다. 따라서 열거한 상수의 참 조값으로 비교( == )하면 된다
public class ClassGradeEx {
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
- discount() 를 호출할 때 미리 정의한 ClassGrade 의 상수를 전달한다.
private 생성자
그런데 위 방식은 외부에서 임의로 ClassGrade 의 인스턴스를 생성할 수 있다는 문제가 있다.
public class ClassGradeEx {
public static void main(String[] args) {
int price = 10000;
DiscountService discountService = new DiscountService();
ClassGrade newClassGrade = new ClassGrade(); //생성자 private으로 막아야 함
int result = discountService.discount(newClassGrade, price);
System.out.println("newClassGrade 등급의 할인 금액: " + result);
}
}
--실행 결과--
할인X
newClassGrade 등급의 할인 금액: 0
이 문제를 해결하려면 기본 생성자를 private 로 설정해 외부에서 ClassGrade 를 생성할 수 없도록 막으면 된다.
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 생성자 추가
private ClassGrade() {}
}
장점
- 타입 안정성 향상
- 정해진 객체만 사용할 수 있기 때문에, 잘못된 값을 입력하는 문제를 근본적으로 방지할 수 있다.
- 데이터 일관성
- 정해진 객체만 사용하므로 데이터의 일관성이 보장된다.
열거형 - Enum Type
자바는 타입 안전 열거형 패턴을 매우 편리하게 사용할 수 있는 열거형을 제공한다.
public enum Grade {
BASIC, GOLD, DIAMOND
}
- 열거형을 정의할 때는 class 대신에 enum 을 사용한다.
- 원하는 상수의 이름을 나열하면 된다.
자바의 열거형으로 작성한 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() {}
}
자바의 열거형을 사용해서 코드를 작성해보자.
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;
}
}
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
열거형(ENUM) 의 장점
- 타입 안정성 향상
- 열거형은 사전에 정의된 상수들로만 구성되므로, 유효하지 않은 값이 입력될 가능성이 없다.
- 이런 경우 컴파일 오류가 발생한다.
- 간결성 및 일관성
- 열거형을 사용하면 코드가 더 간결하고 명확해지며, 데이터의 일관성이 보장된다.
- 확장성
- 새로운 회원 등급을 타입에 추가하고 싶을 때, ENUM 에 새로운 상수를 추가하기만 하면 된다.
열거형 - 주요 메서드
모든 열거형은 java.lang.Enum 클래스를 자동으로 상속 받느다.
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
주요 메서드
- values() : 모든 ENUM 상수를 포함하는 배열을 반환한다.
- valueOf(String name) : 주어진 이름과 일치하는 ENUM 상수를 반환한다.
- name() : ENUM 상수의 이름을 문자열로 반환한다.
- ordinal() : ENUM 상수의 선언순서를 0부터 시작하고 반환한다.
- 중간에 상수를 선언하는 위치가 변경되면 전체 상수의 위치가 모두 변경될 수 있다.
- toString() : ENUM 상수의 이름을 문자열로 반환한다.
- 직접 오버라이드 할 수 있다.
열거형 정리
- java.lang.Enum 을 자동으로 상속 받는다.
- 다른 클래스 상속을 받을 수 없다.
- 인터페이스를 구현할 수 있다.
- 열거형에 추상 메서드를 선언하고, 구현할 수 있다.
'인프런 > 김영한 자바' 카테고리의 다른 글
| [김영한의 실전 자바 중급 1편] - 7. 중첩 클래스, 내부 클래스 (0) | 2025.12.14 |
|---|---|
| [김영한의 실전 자바 중급 1편] - 6. 날짜와 시간 (0) | 2025.10.16 |
| [김영한의 실전 자바 중급 1편] - 4. 래퍼,Class 클래스 (0) | 2025.10.12 |
| [김영한의 실전 자바 중급 1편] - 3. String 클래스 (0) | 2025.10.10 |
| [김영한의 실전 자바 중급 1편] - 2. 불변 객체 (0) | 2025.10.10 |