본 문서는 2021년 12월 22일 에 작성되었습니다.
열거형 은 매우 매력적인 타입입니다.
카테고리 와 같이 고정되어있는 데이터들을 상수로 넘길 때와 문자열로 넘길 때의 장단점을 모두 해결하는 강력한 힘을 가지고 있습니다. 이에 대해서는 Java : Enum을 참고해주세요.
특히 Java 열거형 은 값 뿐만 아니라 열거형 본인의 타입까지 비교합니다.
이로 인해 직관적이면서도 안정적으로 작용을 합니다.
그렇기 때문에 Effective 시리즈 곳곳에서 열거형을 사용하고 있었습니다.
하지만 내가 열거형을 사용하려고 하면 막상 예시가 떠오르지 않았습니다.
따라서 이번 챕터를 진행하며 열거형의 규칙이나 기법에 대한 응용을 보고
작은 아이디어를 얻어갔으면 좋겠습니다.
본 내용은 2021년 12월 22일 에 작성되었습니다.
여기서는 다음과 같은 내용을 다루고 있습니다.
1번 방법으로 할 시, case 열거형: 을 추가하지 않으면 예상하지 못한 결과를 받을 수 있다.
2번 방법으로 할 시, 추상 메서드의 생성 강제로 절대로 잊어버리지 않을 수 있다.
public enum Operation {
PLUS {
public double apply(double x,double y) {
return x+y;
}
},
MINUS { /* return 프로세스 제외하고 동일 */ }
TIMES { /* return 프로세스 제외하고 동일 */ }
DIVIDE { /* return 프로세스 제외하고 동일 */ }
public abstract double apply(double x, double y);
}
예상 가능한 프로세스는 다음과 같습니다.
public enum Day {
// 1차 카테고리 열거형 선언
MONDAY(WEEKDAY),
TUESDAY(WEEKDAY),
WEDNESDAY(WEEKDAY),
THURSDAY(WEEKDAY),
FRIDAY(WEEKDAY),
SATURDAY(WEEKEND),
SUNDAY(WEEKEND);
Day(DayCategory dayCategory){
this.dayCategory=dayCategory;
}
// 1차 카테고리 열거형이 가지게 될 2차 카테고리 열거형 선언
private final DayCategory dayCategory;
enum DayCategory {
WEEKDAY{
int overtimePay(int workMins, int hourWedge){
return (workMins <= MINS_PERSHIFT) ? 0 : (workMins - MINS_PER_SHFIT) * hourWedge / 2;
}
},
WEEKEND{
int overtimePay(int workMins, int hourWedge){
return workMins * hourWedge / 2;
}
};
// 초고근무수당 계산(근무분 * 시간당 돈)
asstract int overtimePay(int workMins, int hourWedge);
int totalPay(int workMins, int hourWedge) {
int basicPay=workMins*hourWedge;
return basicPay+overtimePay(workMins,hourWedge);
}
}
// 2차 카테고리 열거형 타입을 매개변수로 받는 1차 카테고리 열거형 생성자
}
원제목 | ordinal() 메서드 대신 인스턴스 필드를 사용하라
대부분 열거 타입 상수는 자연스럽게 하나의 정수값에 대응된다.
그리고 모든 열거 타입 상수는 ordinal() 로 몇 번째 위치인지 출력할 수 있다.
그러나 ordinal() 이라는 메서드에 의존하면 제한적인 것들이 많다.
아래의 열거형에 세쌍둥이을 넣고싶으면 아이세명과 대응되는 상수 값이 겹쳐지는 것이다.
public enum Ensemble {
아이한명, 아이두명, 아이세명, 아이네명;
public int numberOfKid() {return ordinal()+1; }
}
그렇기 때문에 열거 타입 상수의 필드(Field, 바디)를 따로 열어서 그 안에 변수를 넣어두는 것이 합당하다.
Enum API 에서는 ordinal() 의 설계 목적이 EnumSet 과 EnumMap 과 같은 열거형 범용 자료구조를 위함이며, 대부분의 프로그래머가 직접적으로 사용할 일은 없다 라고 적혀있다.
public enum Esemble {
아이한명(1), 아이두명(2), 쌍둥이(2), 아이세명(3), 세쌍둥이(3), 아이네명(4);
private final int countOfKids;
public int countOfKid() { return countOfKids; }
Esemble(int sizeOfValue) {
this.countOfKids=sizeOfValue;
}
}
원제목 | 비트 필드 대신 EnumSet 을 사용하라
일반적인 사칙연산자 말고도 비트연산은 강점을 가지고 있습니다.
이러한 비트 연산들을 한 필드 안에 선언해둔 것을 비트 필드 라고 하며,
이를 통해서 특정한 연산 (ex. 합집합과 교집합 (집합연산) ) 을 효율적으로 수행할 수 있습니다.
public class bitOperation {
public static final int STYLE_BOLD = 1<<0; // 1
public static final int STYLE_ITALIC = 1<<1; // 2
public static final int STYLE_UNDERLINE = 1<<2; // 4
public static final int STYLE_STRIKETHROUGH = 1<<3; // 8
}
그러나 이러한 상수 선언형 비트 필드는 다음과 같은 단점을 가지고 있습니다.
EnumSet 은 다음과 같은 특징이 있습니다.
EnumSet 만 가지는 특징이 있습니다.
EnumSet 의 단점은 다음과 같습니다.
위 단점은 다음과 같은 방법으로 해결할 수 있지만 명확성과 성능이 희생됩니다.
본 문서는 2021년 12월 23일 에 작성되었습니다.
우리가 쉽게 경험할 수 있는 @Override 같은 어노테이션부터 다양한 것들에 대한 내용을 담고 있습니다.
원제목 | 명명 패턴 보다 어노테이션을 사용하라
본 내용은 JUnit 4 와 관련된 어노테이션을 예로 들고 있습니다.
아직 JUnit 4 를 배우지 않은 관계로 본 내용은 넘어갔습니다.
원제목 | @Override 어노테이션을 일관되게 사용하라
@Override 는 부모 클래스의 메서드를 자식 클래스에서 재정의했음을 알리는 어노테이션이다.
컴파일러는 해당 어노테이션을 통해 당신이 실수했을 때 바로 알려줄 것입니다.
따라서 모든 재정의 메서드에 @Override 를 붙이도록 해야 합니다.
또한 이클립스 같은 IDEs 를 이용한다면 환경 설정을 통해 @Override 를 강제할 수도 있습니다.
유일하게 @Override 를 붙여도 되지 않는 경우는
부모 클래스의 추상 메서드를 재정의하는 순간입니다.
이 경우는 어차피 구현하지 않을 경우에 에러가 발생하므로 @Override 를 쓰지 않아도 됩니다.
하지만 일관성 있게 붙이는 것이 보기 좋다면 붙이지 않아도 됩니다.
원제목 | 정의하려는 것이 타입이라면 마커 인터페이스를 이용해라
마커 인터페이스 는 아무 (추상) 메서드가 없는 인터페이스 입니다.
따라서 자신을 구현하는 클래스가 특정 속성임을 의미하기만 합니다.
마커 어노테이션 은 이러한 기능을 어노테이션의 형태로 하는 것입니다.
하지만 Effective Java 에서는 다음과 같은 이유로 마커 인터페이스를 권고 하고 있습니다.
구현체의 클래스들을 구분하는 타입으로의 기능성
1.a. 마커 인터페이스는 타입 구분자로 쓸 수 있다.
1.b. 마커 어노테이션은 타입 구분자로 슬 수 없다.
에러의 발생시기
2.a. 마커 인터페이스는 컴파일 타임 에 에러가 발생한다.
2.b. 마커 어노테이션은 런타임 에 에러가 발생한다.
그러나 어노테이션은 거대한 어노테이션 시스템의 도움 을 받을 수 있습니다.
따라서 어노테이션을 적극 사용하는 프레임 워크를 사용한다면 권고 될 수 있을 것 같습니다.
아이템 22 | 인터페이스 타입 정의 에서 말한 타입을 정의할 경우에만 인터페이스를 사용하라.
그 외의 값들을 저장하려면 유틸리티 클래스를 사용하라 와 매우 연관성이 깊은 부분이었다.
또한 본 문서의 아이템 34 | 열거형 권장 과도 유사했다.