본문 바로가기

분류 전체보기

(214)
[아이템 37] ordinal 인덱싱 대신 EnumMap을 사용하라 [Why] 배열이나 리스트에서 원소를 꺼낼 때 ordinal 메서드(아이템35)로 인덱스를 얻는 코드가 있다. 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; } } 이제 정원에 심은 식물들을 배열 하나로 관리하고, 이들을 생애주기(한해살이, 여러해살이, 두해살이)별로 묶어보자. Set[] plantsByLifeCyc..
[아이템 36] 비트 필드 대신 EnumSet을 사용하라 [Why] public class Text { public static final int STYLE_BOLD = 1
[아이템 35] ordinal 메서드 대신 인스턴스 필드를 사용하라 ordinal 메서드란? 대부분의 열거 타입 상수는 자연스럽게 하나의 정숫값에 대응된다. 그리고 모든 열거 타입은 해당 상수가 그 열거 타입에서 몇 번째 위치인지를 반환하는 ordinal이라는 메서드를 제공한다. [Why] 열거 타입 상수와 연결된 정숫값이 필요하면 ordinal 메서드를 이용하고 싶은 유혹에 빠진다. public enum Ensemble { SOLO, DUET, TRIO, QUARTET, QUINTET, SEXTET, SEPTET, OCTET, NONET, DECTET; public int numberOfMusicians() { return ordinal() + 1; } } 상수 선언 순서를 바꾸는 순간 numberOfMusicians가 오동작하며, 이미 사용 중인 정수와 값이 같은 상..
[아이템 34] int 상수 대신 열거 타입을 사용하라 [Why] public static final int APPLE_FUJI = 0; public static final int APPLE_PIPPIN = 1; public static final int APPLE_GRANNY_SMITH = 2; public static final int ORANGE_NAVEL = 0; public static final int ORANGE_TEMPLE = 1; public static final int ORANGE_BLOOD = 2; 정수 열거 패턴(int enum pattern) 기법에는 단점이 많다. 타입 안전을 보장할 방법이 없다. 오렌지를 건네야 할 메서드에 사과를 보내고 동등 연산자(==)로 비교하더라도 컴파일러는 아무런 경고 메시지를 출력하지 않는다. 자바가 정..
[아이템 33] 타입 안전 이종 컨테이너를 고려하라 [Why] 제네릭을 쓸 때 매개변수화되는 대상은 원소가 아닌 컨테이너 자신이다. 따라서 하나의 컨테이너에서 매개변수화할 수 있는 타입의 수가 제한된다. 예컨대, Set에는 원소의 타입을 뜻하는 단 하나의 타입 매개변수만 있으면 되며, Map에는 키와 값을 뜻하는 2개만 필요한 식이다. 하지만 더 유연한 수단이 필요할 때도 종종 있다. 예컨대 데이터베이스의 행(row)은 임의 개수의 열(column)을 가질 수 있는데, 모든 열을 타입 안전하게 이용한다면 좋을 것이다. Map map1 = new HashMap(); // ValueType이 한번 고정되면 ValueType만 넣어야 함 Map map2 = new HashMap(); // Object에는 아무거나 들어갈 수 있으나 타입 안전하지 않음. map2..
[아이템 32] 제네릭과 가변인수를 함께 쓸 때는 신중하라 [Why] 가변인수는 메서드에 넘기는 인수의 개수를 클라이언트가 조절할 수 있게 해주는데, 구현 방식에 허점이 있다. 가변인수 메서드를 호출하면 가변인수를 담기 위한 배열이 자동으로 하나 만들어진다. 그런데 내부로 감춰야 했을 이 배열을 그만 클라이언트에 노출하는 문제가 생겼다. 그 결과 varargs 매개변수에 제네릭이나 매개변수화 타입이 포함되면 알기 어려운 컴파일 경고가 발생한다. static void dangerous(List... stringLists) { List intList = List.of(42); Object[] objects = stringLists; objects[0] = intList; // 힙 오염 발생 String s = stringLists[0].get(0); // Class..
[아이템 31] 한정적 와일드카드를 사용해 API 유연성을 높이라 유연성을 극대화하려면 원소의 생산자나 소비자용 입력 매개변수에 와일드카드 타입을 사용하라. [Why] 매개변수화 타입은 불공변(invariant)이다. (아이템28) 서로 다른 타입 Type1과 Type2가 있을 때 List은 List의 하위 타입도 상위 타입도 아니다. List은 List의 하위 타입이 아니라는 뜻인데, 곰곰이 따져보면 사실 이쪽이 말이 된다. List은 List가 하는 일을 제대로 수행하지 못하니 하위 타입이 될 수 없다. (리스코프 치환 원칙에 어긋난다. 아이템10) 하지만 때론 불공변 방식보다 유연한 무언가가 필요하다. public void pushAll(Iterable src) { for (E e : src) { push(e); } } 일련의 원소를 스택에 넣는 메서드이다. 이 ..
[아이템 30] 이왕이면 제네릭 메서드로 만들라 클래스와 마찬가지로, 메서드도 제네릭으로 만들 수 있다. 매개변수화 타입을 받는 정적 유틸리티 메서드는 보통 제네릭이다. [How] 제네릭 메서드 만드는 법 메서드 선언에서의 원소 타입을 타입 매개변수로 명시하고, 메서드 안에서도 이 타입 매개변수만 사용하게 수정하면 된다. 타입 매개변수들을 선언하는 타입 매개변수 목록은 메서드의 제한자와 반환 타입 사이에 온다. public static Set union(Set s1, Set s2) { Set result = new HashSet(s1); result.addAll(s2); return result; } union 메서드는 집합 3개(입력 2개, 반환 1개)의 타입이 모두 같아야 한다. (하위 타입도 안 됨. 완전히 타입이 같아야 함!) 이를 한정적 와일..