250x250
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 이팩티브 자바
- AWS RDS
- CleanCode
- 이펙티브 자바
- 인덱스
- 스프링부트와AWS로혼자구현하는웹서비스
- 혼공SQL
- 알고리즘분석
- 도메인 주도 개발 시작하기
- aop
- 자바스터디
- 인프런김영한
- 기술면접
- jpa
- java
- 자바예외
- 네트워크
- mysql
- 이펙티브자바
- MariaDB
- 클린코드
- vue.js
- SQL쿡북
- 자바
- 인프런백기선
- react
- 알고리즘
- 자료구조
- DDD
- AWS
Archives
- Today
- Total
기록이 힘이다.
[이펙티브 자바] 6장 열거 타입과 애너테이션 본문
728x90
아이템37 ordinal 인덱싱 대신 EnumMap을 사용하라
class Plant {
enum LifeCycle { ANNUAL, PERENNIAL, BIENNIAL }
final String name;
final LifeCycle lifeCycle;
Plant(String name, LifeCycle lifeCycle) {
this.name = name;
this.lifeCycle = lifeCycle;
}
@Override public String toString() {
return name;
}
public static void main(String[] args) {
Plant[] garden = {
new Plant("바질", LifeCycle.ANNUAL),
new Plant("캐러웨이", LifeCycle.BIENNIAL),
new Plant("딜", LifeCycle.ANNUAL),
new Plant("라벤더", LifeCycle.PERENNIAL),
new Plant("파슬리", LifeCycle.BIENNIAL),
new Plant("로즈마리", LifeCycle.PERENNIAL)
};
// 코드 37-1 ordinal()을 배열 인덱스로 사용 - 따라 하지 말 것! (226쪽)
Set<Plant>[] plantsByLifeCycleArr =
(Set<Plant>[]) new Set[Plant.LifeCycle.values().length];
for (int i = 0; i < plantsByLifeCycleArr.length; i++)
plantsByLifeCycleArr[i] = new HashSet<>();
for (Plant p : garden)
plantsByLifeCycleArr[p.lifeCycle.ordinal()].add(p);
// 결과 출력
for (int i = 0; i < plantsByLifeCycleArr.length; i++) {
System.out.printf("%s: %s%n",
Plant.LifeCycle.values()[i], plantsByLifeCycleArr[i]);
}
// 코드 37-2 EnumMap을 사용해 데이터와 열거 타입을 매핑한다. (227쪽)
Map<Plant.LifeCycle, Set<Plant>> plantsByLifeCycle =
new EnumMap<>(Plant.LifeCycle.class);
for (Plant.LifeCycle lc : Plant.LifeCycle.values())
plantsByLifeCycle.put(lc, new HashSet<>());
for (Plant p : garden)
plantsByLifeCycle.get(p.lifeCycle).add(p);
System.out.println(plantsByLifeCycle);
내부 구현 방식을 안으로 숨겨서 Map의 타입 안전성과 배열의 성능을 모두 얻어낸 것이다.
// 코드 37-3 스트림을 사용한 코드 1 - EnumMap을 사용하지 않는다! (228쪽)
System.out.println(Arrays.stream(garden)
.collect(groupingBy(p -> p.lifeCycle)));
// 코드 37-4 스트림을 사용한 코드 2 - EnumMap을 이용해 데이터와 열거 타입을 매핑했다. (228쪽)
System.out.println(Arrays.stream(garden)
.collect(groupingBy(p -> p.lifeCycle,
() -> new EnumMap<>(LifeCycle.class), toSet())));
EnumMap 버전은 언제나 식물의 생애주기당 하나씩의 중첩 맵을 만들지만, 스트림 버전에서는 해당 생애주기에 속하는 식물이 있을 때만 만든다. 예컨대 정원에 한해살이와 여러해살이 식물만 살고 두해살이는 없다면, EnumMap 버전에서는 맵을 3개 만들고 스트림 버전에서는 2개만 만든다.
아이템38. 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라
package effectivejava.chapter6.item38;
// 코드 38-1 인터페이스를 이용해 확장 가능 열거 타입을 흉내 냈다. (232쪽)
public interface Operation {
double apply(double x, double y);
}
package effectivejava.chapter6.item38;
import java.util.*;
// 코드 38-2 확장 가능 열거 타입 (233-235쪽)
public enum ExtendedOperation implements Operation {
EXP("^") {
public double apply(double x, double y) {
return Math.pow(x, y);
}
},
REMAINDER("%") {
public double apply(double x, double y) {
return x % y;
}
};
private final String symbol;
ExtendedOperation(String symbol) {
this.symbol = symbol;
}
@Override public String toString() {
return symbol;
}
// // 열거 타입의 Class 객체를 이용해 확장된 열거 타입의 모든 원소를 사용하는 예 (234쪽)
// public static void main(String[] args) {
// double x = Double.parseDouble(args[0]);
// double y = Double.parseDouble(args[1]);
// test(ExtendedOperation.class, x, y);
// }
// private static <T extends Enum<T> & Operation> void test(
// Class<T> opEnumType, double x, double y) {
// for (Operation op : opEnumType.getEnumConstants())
// System.out.printf("%f %s %f = %f%n",
// x, op, y, op.apply(x, y));
// }
// 컬렉션 인스턴스를 이용해 확장된 열거 타입의 모든 원소를 사용하는 예 (235쪽)
public static void main(String[] args) {
double x = Double.parseDouble(args[0]);
double y = Double.parseDouble(args[1]);
test(Arrays.asList(ExtendedOperation.values()), x, y);
}
private static void test(Collection<? extends Operation> opSet,
double x, double y) {
for (Operation op : opSet)
System.out.printf("%f %s %f = %f%n",
x, op, y, op.apply(x, y));
}
}
아이템40. @Override 애너테이션을 일관되게 사용하라
package effectivejava.chapter6.item40;
import java.util.*;
// 코드 40-1 영어 알파벳 2개로 구성된 문자열(바이그램)을 표현하는 클래스 - 버그를 찾아보자. (246쪽)
public class Bigram {
private final char first;
private final char second;
public Bigram(char first, char second) {
this.first = first;
this.second = second;
}
public boolean equals(Bigram b) {
return b.first == first && b.second == second;
}
public int hashCode() {
return 31 * first + second;
}
public static void main(String[] args) {
Set<Bigram> s = new HashSet<>();
for (int i = 0; i < 10; i++)
for (char ch = 'a'; ch <= 'z'; ch++)
s.add(new Bigram(ch, ch));
System.out.println(s.size());
}
}
메서드가 '재정의(Overriding)'한 게 아니라 '다중정의(overloading)' 해버렸다. 매개변수 타입을 Object로 설정해줘야 하는데 다른 타입이 들어와 있다. 따라서 예상하는 갯수가 나오지 않는다.
package effectivejava.chapter6.item40;
import java.util.HashSet;
import java.util.Set;
// 버그를 고친 바이그램 클래스 (247쪽)
public class Bigram2 {
private final char first;
private final char second;
public Bigram2(char first, char second) {
this.first = first;
this.second = second;
}
@Override public boolean equals(Object o) {
if (!(o instanceof Bigram2))
return false;
Bigram2 b = (Bigram2) o;
return b.first == first && b.second == second;
}
public int hashCode() {
return 31 * first + second;
}
public static void main(String[] args) {
Set<Bigram2> s = new HashSet<>();
for (int i = 0; i < 10; i++)
for (char ch = 'a'; ch <= 'z'; ch++)
s.add(new Bigram2(ch, ch));
System.out.println(s.size());
}
}
상위 클래스의 메서드를 재정의하려는 모든 메서드에 @Override 애너테이션을 달자. 예외는 한 가지뿐이다. 구체 클래스에서 상위 클래스의 추상 메서드를 재정의할 때는 굳이 @Override를 달지 않아도 된다. 단다고 해서 해로울 건 없다.
'JAVA' 카테고리의 다른 글
리플렉션보다 인터페이스를 사용하라. (0) | 2023.04.24 |
---|---|
[이펙티브자바] 59. 라이브러리를 익히고 사용하라 (0) | 2023.04.21 |
[이펙티브 자바] 58. 전통적인 for문보다는 for-each문을 사용하라 (0) | 2023.04.20 |
[이펙티브 자바] 56. 공개된 API 요소에는 항상 문서화 주석을 작성하라 (0) | 2023.04.19 |
왜 우리는 비검사 예외를 선호하는가?(UncheckedException) (0) | 2023.04.13 |