본문 바로가기

분류 전체보기

(214)
[아이템 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)으..
[아이템 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] 람..