우아한테크코스 6기 프리코스 중 3주차 과제를 통해 enum을 어떻게 객체로 활용할지에 대한 고민이 많았다. 4주차에도 이를 확실히 활용할 수 있도록 자세히 파헤쳐보려한다.
✔️ enum이란?
Enum은 Enumeration의 약자다. 즉, 열거형은 요소, 멤버라 불리는 명명된 값의 집합을 이루는 자료형이다. 정해져있는 한정된 데이터 묶음을 열거형 타입인 Enum으로 묶어주면 보다 구조적으로 프로그래밍 할 수 있다. Enum의 장점은 다음과 같다.
1. 문자열과 비교해 IDE의 적극적인 지원을 받을 수 있다.
- 자동완성, 오타검증, 테스트 리팩토링 등
2. 허용 가능한 값들을 제한하여 유형 안전(type safe)을 제공한다.
3. 리팩토링 시 변경 범위가 최소화된다.
- 내용의 추가가 필요하더라도, Enum 코드 이외에 수정할 필요가 없다.
4. 키워드 enum을 사용하기 때문에 구현의 의도가 열거임을 분명하게 나타낼 수 있다.
✔️ enum 메서드 종류
메서드 | 설명 | 리턴 타입 |
name() | 열거 객체의 문자열을 리턴 | String |
ordinal() | 열거 객체의 순번(0부터 시작)을 리턴 | int |
compareTo() | 열거 객체를 비교해서 순번 차이를 리턴 | int |
valueOf(String name) | 문자열을 입력받아서 일치하는 열거 객체를 리턴 | enum |
values() | 모든 열거 객체들을 배열로 리턴 | enum[] |
name() 메서드
Fruit f = Fruit.APPLE;
// 열거 객체의 문자열 리턴
String fruitName = f.name();
System.out.println(fruitName); // Apple
ordinal() 메서드
Fruit f = Fruit.APPLE;
// 열거 객체의 순번 리턴
int fruitNumber = f.ordinal();
System.out.println(fruitNumber); // 1
compareTo() 메서드
// 열거 객체를 비교해서 순번 차이를 리턴 (시작점을 어느 열거 객체의 기준으로 몇번째 위치하는지)
Fruit f1 = Fruit.APPLE; // 1
Fruit f2 = Fruit.BANANA; // 2
// 열거 객체가 매개값의 열거 객체보다 순번이 빠르다 → 음수를 리턴
int compare1 = f1.compareTo(f2); // BANANA를 기준으로 APPLE 위치 (2에서 1가 되기 위한 값)
System.out.println(compare1); // -1
// 열거 객체가 매개값의 열거 객체보다 순번이 늦다 → 양수를 리턴
int compare2 = f2.compareTo(f1); // APPLE 기준으로 BANANA 위치 (1에서 2가 되기 위한 값)
System.out.println(compare2); // 1
valueOf(String name) 메서드
// 문자열을 입력받아서 일치하는 열거 객체를 리턴
Fruit f3 = Fruit.valueOf("MANGO"); // f3 변수는 Fruit.MANGO 열거 객체를 참조하게 됨
System.out.println(f3); // MANGO
values() 메서드
// 모든 열거 객체들을 배열로 리턴
Fruit[] f4 = Fruit.values();
System.out.println(Arrays.toString(f4)); // [APPLE, BANANA, MANGO]
for (Fruit type : Fruit.values()) { // 열거 순회
System.out.println(type); // 순서대로 열거 객체 출력
}
✔️ enum 매핑
enum은 데이터 그룹화 및 관리에 용이하다. 즉, 서로 관련되어있는 데이터들을 한 클래스 내에서 관리할 수 있다. 다음은 3주차 과제 로또 게임의 일부 코드이다.
public enum WinningStatistics {
FIRST(6, 2_000_000_000, false, "\n6개 일치 (2,000,000,000원) - "),
SECOND(5, 30_000_000, true, "\n5개 일치, 보너스 볼 일치 (30,000,000원) - "),
THIRD(5, 1_500_000, false, "\n5개 일치 (1,500,000원) - "),
FOURTH(4, 50_000, false, "\n4개 일치 (50,000원) - "),
FIFTH(3, 5_000, false, "3개 일치 (5,000원) - "),
NOTHING(0, 0, false, "");
private final int matchCount;
private final int reward;
private final boolean isContainBonus;
private final String message;
WinningStatistics(int matchCount, int reward, boolean isContainBonus, String message) {
this.matchCount = matchCount;
this.reward = reward;
this.isContainBonus = isContainBonus;
this.message = message;
}
}
✔️ enum 확장
enum 매핑 기능을 확장하여 enum을 단순히 상수 값을 넘어서 상수 메서드로서 사용할 수 있다.
위 코드에서 enum 매핑은 클래스 필드와 생성자를 정의해서 상수의 매개변수 값을 필드에 저장하고 인스턴스화 함으로써 고유한 상수 객체로서 활용했다.
여기서 더 나아가 추상 메서드를 정의해 각 상수마다 익명 클래스(프로그램에서 일시적으로 한 번만 사용되고 버려지는 객체)처럼 메서드 재정의를 통해 각 상수마다 다른 역할을 하는 메서드를 갖게 되는 원리이다.
enum Operation {
PLUS("+") {
public double apply(double x, double y) {
return x + y;
}
},
MINUS("-") {
public double apply(double x, double y) {
return x - y;
}
},
MULTI("*") {
public double apply(double x, double y) {
return x * y;
}
},
DIVIDE("/") {
public double apply(double x, double y) {
return x / y;
}
};
// 클래스 생성자와 멤버
private final String symbol;
Operation(String symbol) {
this.symbol = symbol;
}
// toString을 재정의하여 열거 객체의 매핑된 문자열을 반환하도록
@Override
public String toString() {
return symbol;
}
// 열거 객체의 메소드에 사용될 추상 메소드 정의
public abstract double apply(double x, double y);
}
enum은 상태와 행위 모두 한 곳에서 관리 가능
이처럼 enum은 단순히 상수 표현식을 넘어서 동작, 행위를 수행하는 상수로서도 응용 확장이 가능하다. 상수 상태와 행위를 한 곳에서 관리할 수 있는 것이다.
예를 들어 각 상수를 특정 데이터와 연결짓거나, 상수마다 다르게 동작해야하는 로직이 필요할 때 일반적으로 if문 또는 switch문으로 구현하기 마련이다. 그러나 이러한 코드 구성은 값(상태)과 메서드(행위)가 어떤 관계가 있는지 파악하기 어렵다. 하지만 enum을 이용하면 상태와 행위를 한 눈에 파악할 수 있다. 즉, 객체지향적으로 코드를 패턴화하여 유지보수하기 용이하게 만들 수 있다.
enum Operation {
PLUS("+") {
public double apply(double x, double y) {
return x + y;
}
},
MINUS("-") {
public double apply(double x, double y) {
return x - y;
}
},
MULTI("*") {
public double apply(double x, double y) {
return x * y;
}
},
DIVIDE("/") {
public double apply(double x, double y) {
return x / y;
}
};
}
enum에 람다식 사용
enum은 람다식을 통해 위 코드를 더욱 깔끔하게 만들 수 있다.
enum Operation {
PLUS("+", (x, y) -> x + y),
MINUS("-", (x, y) -> x - y),
TIMES("*", (x, y) -> x * y),
DIVIDE("/", (x, y) -> x / y);
private final DoubleBinaryOperator op; // 람다식을 저장할 필드
private final String symbol;
Operation(DoubleBinaryOperator op, String symbol) {
this.op = op;
this.symbol = symbol;
}
// toString을 재정의하여 열거 객체의 매핑된 문자열을 반환하도록
@Override
public String toString() {
return symbol;
}
// 열거 객체의 메소드에 사용될 추상 메소드 정의
public double apply(double x, double y) {
return op.apllyAsDouble(x, y);
}
}
위 내용은 다음 블로그들을 참고하였습니다.
https://tecoble.techcourse.co.kr/post/2020-07-29-dont-use-else/
https://techblog.woowahan.com/2527/
'☕️ Java' 카테고리의 다른 글
[Java] Stream (4) | 2023.11.09 |
---|---|
[Java] 일급 컬렉션 (2) | 2023.10.26 |