본문 바로가기

독서찰기(讀書札記)/이펙티브 자바

(73)
[아이템 44] 표준 함수형 인터페이스를 사용하라 @FunctionalInterface Functional Interface는 구현해야 할 추상 메소드가 하나만 정의된 인터페이스를 가리킨다. [Why] 자바가 람다를 지원하면서 API를 작성하는 모범 사례도 크게 바뀌었다. 예컨대 상위 클래스의 기본 메서드를 재정의해 원하는 동작을 구현하는 템플릿 메서드 패턴의 매력이 크게 줄었다. 이를 대체하는 현대적인 해법은 같은 효과의 함수 객체를 받는 정적 팩터리나 생성자를 제공하는 것이다. 따라서 함수 객체를 매개변수로 받는 생성자와 메서드를 많이 만들어야 한다. 이때 함수형 매개변수 타입을 올바르게 선택해야 한다. protected boolean removeEldestEntry(Map.Entry eldest) { return size() > 100; } // ..
[아이템 43] 람다보다는 메서드 참조를 사용하라 [Why] 람다가 익명 클래스보다 나은 점 중에서 가장 큰 특징은 간결함이다. 그런데 자바에는 함수 객체를 심지어 람다보다도 더 간결하게 만드는 방법이 있으니, 바로 메서드 참조(method reference)다. map.merge(key, 1, (count, incr) -> count + incr); 위 코드는 임의의 키와 Integer 값의 매핑을 관리하는 프로그램의 일부다. 이 코드는 자바 8 때 Map에 추가된 merge 메서드를 사용했다. merge 메서드는 키, 값, 함수를 인수로 받으며, 주어진 키가 맵 안에 아직 없다면 주어진 {키, 값} 쌍을 그대로 저장한다. 반대로 키가 이미 있다면 세 번째 인수로 받은 함수를 현재 값과 주어진 값에 적용한 다음, 그 결과로 현재 값을 덮어쓴다. 즉, ..
[아이템 42] 익명 클래스보다는 람다를 사용하라 ※ 예전에는 자바에서 함수 타입을 표현할 때 추상 메서드를 하나만 담은 인터페이스(드물게는 추상 클래스)를 사용했다. 이런 인터페이스의 인스턴스를 함수 객체(function object)라고 하여, 특정 함수나 동작을 나타내는 데 썼다. 1997년 JDK 1.1이 등장하면서 함수 객체를 만드는 주요 수단은 익명 클래스(아이템24)가 되었다. [Why] Collections.sort(words, new Comparator() { public int compare(String s1, String s2) { return Integer.compare(s1.length(), s2.length()); } }); 익명 클래스 방식은 코드가 너무 길기 때문에 자바는 함수형 프로그래밍에 적합하지 않았다. [When] 람..
[아이템 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..