본문 바로가기

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

(73)
[아이템 52] 다중정의는 신중히 사용하라 [Why] public class CollectionClassifier { public static String classify(Set s) { return "집합"; } public static String classify(List lst) { return "리스트"; } public static String classify(Collection c) { return "그 외"; } public static void main(String[] args) { Collections[] collections = { new HashSet(), new ArrayList(), new HashMap().values() }; for (Collection c : collections) { System.out.println..
[아이템 51] 메서드 시그니처를 신중히 설계하라 ※ API 설계 요령을 설명한다. 1. 메서드 이름을 신중히 짓자. 항상 표준 명명 규칙(아이템68)을 따라야 한다. 이해할 수 있고, 같은 패키지에 속한 다른 이름들과 일관되게 짓는 게 최우선 목표다. 그 다음 목표는 개발자 커뮤니티에서 널리 받아들여지는 이름을 사용하는 것이다. 긴 이름은 피하자. 애매하면 자바 라이브러리의 API 가이드를 참조하자. 2. 편의 메서드를 너무 많이 만들지 말자. 메서드가 너무 많은 클래스는 익히고, 사용하고, 문서화하고, 테스트하고, 유지보수하기 어렵다. (인터페이스도 마찬가지) 3. 매개변수 목록은 짧게 유지하자. 4개 이하가 좋다. 같은 타입의 매개변수 여러 개가 연달아 나오는 경우가 특히 해롭다. 순서를 기억하기 어려울뿐더러, 실수로 순서를 바꿔 입력해도 그대로 ..
[아이템 50] 적시에 방어적 복사본을 만들라 [Why] 자바는 안전한 언어다. 하지만 아무리 자바라 해도 다른 클래스로부터의 침범을 아무런 노력 없이 다 막을 수 있는 건 아니다. 그러니 클라이언트가 여러분의 불변식을 깨뜨리려 혈안이 되어 있다고 가정하고 방어적으로 프로그래밍해야 한다. public final class Period { private final Date start; private final Date end; /** * @param start 시작 시각 * @param end 종료 시각; 시작 시각보다 뒤여야 한다. * @throws IllegalArgumentException 시작 시각이 종료 시각보다 늦을 때 발생한다. * @throws NullPointerException start나 end가 null이면 발생한다. */ pub..
[아이템 49] 매개변수가 유효한지 검사하라 [Why] 메서드 몸체가 실행되기 전에 매개변수를 확인한다면 잘못된 값이 넘어왔을 때 즉각적이고 깔끔한 방식으로 예외를 던질 수 있다. 매개변수 검사를 제대로 하지 못하면 실패 원자성(failure atomicity, 아이템76)을 어기는 결과를 낳을 수 있다. 메서드가 수행되는 중간에 모호한 예외를 던지며 실패할 수 있다. 더 나쁜 상황은, 메서드가 잘 수행되지만 잘못된 결과를 반환할 때다. 이보다 더 나쁜 상황은, 메서드는 문제없이 수행됐지만 어떤 객체를 이상한 상태로 만들어놓아서 미래의 알 수 없는 시점에 이 메서드와는 관련 없는 오류를 낼 때다. [When] 메서드가 직접 사용하지는 않으나 나중에 쓰기 위해 저장하는 매개변수는 특히 더 신경 써서 검사해야 한다. 입력받은 int 배열의 List 뷰..
[아이템 48] 스트림 병렬화는 주의해서 적용하라 [배경] 동시성 프로그래밍 측면에서 자바는 항상 앞서갔다. 1996년, 첫 릴리스부터 스레드, 동기화, wait/notify를 지원했다. 자바 5부터는 동시성 컬렉션인 java.util.concurrent 라이브러리와 실행자(Executor) 프레임워크를 지원했다. 자바 7부터는 고성능 병렬 분해(parallel decom-position) 프레임워크인 포크-조인(fork-join) 패키지를 추가했다. 자바 8부터는 parallel 메서드만 한 번 호출하면 파이프라인을 병렬 실행할 수 있는 스트림을 지원했다. [Why] 동시성 프로그래밍을 할 때는 안전성(safety)과 응답 가능(liveness) 상태를 유지하기 위해 애써야 하는데, 병렬 스트림 파이프라인 프로그래밍에서도 다를 바 없다. public ..
[아이템 47] 반환 타입으로는 스트림보다 컬렉션이 낫다 [배경] 원소 시퀀스, 즉 일련의 원소를 반환하는 메서드는 수없이 많다. 자바 7까지는 이런 메서드의 반환 타입으로 컬렉션 인터페이스, Iterable, 배열을 썼다. 그런데 자바 8이 스트림이라는 개념을 들고 오면서 반환 타입의 선택이 아주 복잡해졌다. 스트림은 반복(iteration)을 지원하지 않는다. 따라서 스트림과 반복을 알맞게 조합해야 좋은 코드가 나온다. 사실 Stream 인터페이스는 Iterable 인터페이스가 정의한 추상 메서드를 전부 포함할 뿐만 아니라, Iterable 인터페이스가 정의한 방식대로 동작한다. 그럼에도 for-each로 스트림을 반복할 수 없는 까닭은 바로 Stream이 Iterable을 확장(extend)하지 않아서다. 어댑터를 사용하면 반환값의 Stream → Ite..
[아이템 46] 스트림에서는 부작용 없는 함수를 사용하라 [Why] 스트림 패러다임의 핵심은 계산을 일련의 변환(transformation)으로 재구성하는 부분이다. 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수 함수여야 한다. 다른 가변 상태를 참조하지 않고, 함수 스스로도 다른 상태를 변경하지 않아야 한다. 이렇게 하려면 (중간 단계든 종단 단계든) 스트림 연산에 건네는 함수 객체는 모두 부작용(side effect)가 없어야 한다. Map freq = new HashMap(); try (Stream words = new Scanner(file).tokens()) { words.forEach(word -> { freq.merge(word.toLowerCase(), 1L, Long::sum); }); } 텍스트 파일에서 단어별 수를 세어 ..
[아이템 45] 스트림은 주의해서 사용하라 스트림이란? 스트림 API는 다량의 데이터 처리 작업(순차적이든 병렬적이든)을 돕고자 자바 8에 추가되었다. 스트림 API가 제공하는 추상 개념 중 핵심은 두 가지다. 첫 번째, 스트림(stream)은 데이터 원소의 유한 혹은 무한 시퀀스(sequence)를 뜻한다. 두 번째, 스트림 파이프라인(stream pipeline)은 이 원소들로 수행하는 연산 단계를 표현하는 개념이다. 스트림 안의 데이터 원소들은 객체 참조나 기본 타입 값이다. 기본 타입 값으로는 int, long, double 이렇게 세 가지를 지원한다. 스트림 API는 메서드 연쇄를 지원하는 플루언트 API(fluent API)다. 스트림 파이프라인 스트림 파이프라인은 소스 스트림에서 시작해 종단 연산(terminal operation)으..