본문 바로가기

개발/이펙티브 자바

Effective Java ( 이펙티브 자바 ) - 아이템 46 스트림에서는 부작용 없는 함수를 사용하라 스트림은 처음 봐서는 이해하기 어려울 수 있다. 스트림은 그저 또 하나의 API가 아닌, 함수형 프로그래밍에 기초한 패러다임이다. 스트림 패러다임의 핵심은 계산을 일련의 변환으로 재구성 하는 부분이다. 이때 각 변화 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수 함수여야 한다. 순수 함수란 오직 입력만이 결과에 영향을 주는 함수를 말한다. 다른 가변 상태를 참조하지 않고, 함수 스스로도 다른 상태를 변경하지 않는다. 이렇게 하려면 (중간 단계든 종단 단계든) 스트림 연산에 건네는 함수 객체는 모두 부작용(side effect)이 없어야 한다. 다음은 주위에서 종종 볼 수 있는 스트림 코드로, 텍스트 파일에서 단어별 수를 세어 빈도표로 만드는 일을 한다.. 더보기
Effective Java ( 이펙티브 자바 ) - 아이템 45 스트림은 주의해서 사용하라 스트림은 다량의 데이터 처리 작업을 돕고자 자바8에 추가되었다. 스트림의 추상 개념 중 핵심은 두 가지다. 스트림은 데이터 원소의 유한 혹은 무한 시퀸스를 뜻한다. 스트림 파이프라인은 이 원소들로 수행하는 연산 단계를 표현하는 개념이다. 스트림의 원소들은 어디로부터든 올 수 있다. 스트림 안의 데이터 원소들은 객체 참조나 기본 타입 값이다. int, long, double 이렇게 세 가지를 지원한다. 스트림 파이프라인은 소스 스트림에서 시작해 종단 연산으로 끝나며, 그 사이에 하나 이상의 중간 연산이 있을 수 있다. 각 중간 연산은 스트림을 어떤 방식으로 변환한다. 중간 연산들은 모두 한 스트림을 다른 스트림으로 변환하는데, 변환된 스트림의 원소 타입은 변환 전 스트림의 원소 .. 더보기
Effective Java ( 이펙티브 자바 ) - 아이템 44 표준 함수형 인터페이스를 사용하라 자바가 람다를 지원하면서 API를 작성하는 모범 사례도 크게 바뀌었다. 예컨대 상위 클래스의 기본 메서드를 재정의해 원하는 동작을 구현하는 템플릿 메서드 패턴의 매력이 크게 줄었다. 이를 대체하는 현대적인 해법은 같은 효과의 함수 객체를 받는 정적 팩터리나 생성자를 제공하는 것이다. 이 내용을 일반화해서 말하면 함수 객체를 매개변수로 받는 생성자와 메서드를 더 많이 만들어야 한다. 이때 함수형 매개변수 타입을 올바르게 선택해야 한다. LinkedHashMap을 생각해보자. 이 클래스의 protected 메서드인 removeEldestEntry를 재정의하면 캐시로 사용할 수 있다. 맵에 새로운 키를 추가하는 put 메서드는 이 메서드를 호출하여 true가 반환되면 맵에서 .. 더보기
Effective Java ( 이펙티브 자바 ) - 아이템 43 람다보다는 메서드 참조를 사용하라 람다가 익명 클래스보다 나은 점 중에서 가장 큰 특징은 간결함이다. 그런데 자바에는 함수 객체를 심지어 람다보다도 더 간결하게 만드는 방법이 있으니, 바로 메서드 참조(method reference)다. 다음 코드는 임의의 키와 Integer 값의 매핑을 관리하는 프로그램의 일부다. 이때 값이 키의 인스턴스 개수로 해석된다면, 이 프로그램은 multiset(멀티셋)을 구현한 게 된다. 이 코드는 키가 맵 안에 없다면 키와 숫자 1을 매핑하고, 이미 있다면 기존 매핑 값을 증가시킨다. map.merge(key, 1, (count, incr) -> count + incr); 이 코드는 자바 8 때 Map에 추가된 merge 메서드를 사용했다. merge 메서드는 키, 값, .. 더보기
Effective Java ( 이펙티브 자바 ) - 아이템 42 - 7장 - 람다와 스트림 익명 클래스보다는 람다를 사용하라 예전에는 자바에서 함수 타입을 표현할 때 추상 메서드를 하나만 담은 인터페이스(드물게는 추상 클래스)를 사용했다. 이런 인터페이스의 인스턴스를 함수객체라고 하여, 특정 함수나 동작을 나타내는 데 썼다. 1997 JDK 1.1의 등장으로 인해, 함수 객체를 만드는 주요 수단은 익명 클래스가 되었다. 다음 코드를 예로 살펴보자. 문자열을 길이순으로 정렬하는데, 정렬을 위한 비교 함수로 익명 클래스를 사용한다. Collections.sort(words,new Comparator() { public int compare(String s1, String s2) { return Integer.compare(s1.length(), s2.length()); } }); 함수 객체를 사용.. 더보기
Effective Java ( 이펙티브 자바 ) - 아이템 41 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라 아무 메서드도 담고 있지 않고, 단지 자신을 구현하는 클래스가 특정 속성을 가짐을 표시해주는 인터페이스를 마커 인터페이스라 한다. Serializable 인터페이스가 좋은 예다. Serializable은 자신을 구현한 클래스의 인스턴스는 ObjectOutputStream을 통해 쓸 수 있다고, 즉 직렬화 할 수 있다고 알려준다. 마커 애너테이션이 등장하면서 마커 인터페이스는 구식이 되었다는 이야기를 들어보았을 것이다. 하지만 사실이 아니다. 마커 인터페이스는 두 가지 면에서 마커 애너테이션보다 낫다. 첫 번째, 마커 인터페이스는 이를 구현한 클래스의 인스턴스들을 구분하는 타입으로 쓸 수 있으나, 마커 애너테이션은 그렇지 않다. 마커 인터페이스는 어엿한.. 더보기
Effective Java ( 이펙티브 자바 ) - 아이템 40 @Override 애너테이션을 일관되게 사용하라 자바가 기본으로 제공하는 애너테이션 중 보통의 프로그래머에게 가장 중요한 것은 @Override 일 것이다. 메서드 선언에만 달 수 있으며, 이 애너테이션이 달렸다는 것은 상위 타입의 메서드를 재정의했음을 뜻한다. 이 애너테이션을 일관되게 사용하면 여러 가지 악명 높은 버그들을 예방해준다. 다음의 Bigram 프로그램을 살펴보자. 이 클래스는 바이그램, 즉 여기서는 영어 알파벳 2개로 구성된 문자열을 표현한다. public class Bigram { private final char first; private final char second; public Bigram(char first, char second) { this.first = first; thi.. 더보기
Effective Java ( 이펙티브 자바 ) - 아이템 39 명명 패턴보다 애너테이션 사용하라 전통적으로 도구나 프레임워크가 특별히 다뤄야 할 프로그램 요소에는 딱 구분되는 명명 패턴을 적용해왔다. 예컨대 테스트 프레임워크인 Junit은 버전 3까지 테스트 메서드 이름을 test로 시작하게끔 했다. 효과적인 방법이지만 단점도 크다. 첫 번째, 오타가 나면 안 된다. 실수로 이름을 tsetSafetyOverride로 지으면 Junit 3은 이 메서드를 무시하고 지나치기 때문에 개발자는 이 테스트가 통과했다고 오해할 수 있다. 두 번째, 올바른 프로그램 요소에서만 사용되리라 보증할 방법이 없다는 것이다. 예컨대 클래스 이름을 TestSafetyMechanisms로 지어 Junit에 던져줬다고 해보자. 개발자는 이 클래스에 정의된 테스트 메서드들을 수행해주길 기대하겠지.. 더보기