전체 글
42. 익명 클래스보다는 람다를 사용하라 ( 람다 표현식 )
예전의 익명 클래스 방식은 코드가 너무 길기 때문에 자바는 함수형 프로그래밍에 적합하지 않았다. 자바 8에서 추상 메서드 하나짜리 인터페이는 특별한 의미를 인정받아 함수형 인터페이스의 인스턴스를 람다식을 사용해 만들 수 있게 되었다. 람다는 함수나 익명 클래스와 개념은 비슷하지만 코드는 훨씬 간결하다. 다음은 익명 클래스를 사용한 앞의 코드를 람다 방식으로 바꾼 모습이다. Collection.sort(words, new Comparator() { public int compare(String s1, String s2){ return Integer.compare(s1.length(), s2.length()); } }); Collection.sort(words, (s1,s2) -> Integer.compar..
41. 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라 ( 마커 인터페이스 )
아무 메서드도 담고 있지 않고, 단지 자신을 구현하는 클래스가 특정 속성을 가짐을 표시해주는 인터페이스를 마커 인터페이라 한다. Serializable 인터페이스 좋은 예다. 클래스의 인스턴스는 ObjectOutputStream을 통해 쓸 수 있다고, 즉 직렬화 할 수 있다고 알려준다. 마커 애너테이션이 등장하면서 마커 인터페이스는 구식이 되었다. 하지만 사실이 아니다. 마커 인터페이스는 이를 구현한 클래스의 인스턴스들을 구분하는 타입으로 쓸 수 있다. 또, 적용 대상을 더 정밀하게 지정할 수 있다. 적용 대상을 ElementType.TYPE 으로 선언한 애너테이션은 모든 타입에 달 수 있다. 부착하는 타입을 더 세밀하게 제한하지는 못한다는 뜻이다. Set 인터페이스도 일종의 마커 인터페이스로 볼 수 있..
40. @Override 애너테이션을 일관되게 사용하라 ( @Override )
자바가 기본으로 제공하는 애너테이션 중 보통의 프로그래머에게 가장 중요한 것은 @Override일 것이다. @Override는 메서드 선언에만 달 수 있으며, 이 애너테이션이 달렸다는 것은 상위 타입의 메서드를 재정의했음을 뜻한다. 이 애너테이션을 일관되게 사용하면 여러 가지 악명 높은 버그들을 예방해준다. 상위 클래스의 메서드를 재정의하려는 모든 메서드에 @Override 애너테이션을 달자. 특정 메서드를 재정의할 때 잘 못 구현하면 메서드를 새로 정의하게 되어버린다. @Override 애너테이션을 붙이면 컴파일 할 때 컴파일러가 오류를 찾아낼 수 있다. @Override는 클래스뿐 아니라 인터페이스의 메서드를 재정의할 때도 사용할 수 있다. 재정의한 모든 메서드에 @Override 애너테이션을 의식적..
39. 명명 패턴보다 애너테이션을 사용하라
전통적으로 도구나 프레임워크가 특별히 다뤄야 할 프로그램 요소에는 딱 구분되는 명명 패턴을 적용해왔다. 테스트 프레임워크인 JUnit은 버전 3까지 테스트 메서드 이름을 test로 시작하게끔 했다. 효과적인 방법이지만 단점도 크다. 첫째, 오타가 나면 안된다. 실수로 이름을 test~~...으로 지으면 JUnit3는 이 메서드를 무시하고 지나치기 때문에 개발자는 이 테스트가 통과했다고 오해할 수 있다. 둘째, 올바른 프로그램 요소에서만 사용되리라고 보증할 수 없기 때문이다. 셋째, 프로그램 요소를 매개변수로 전달할 마땅한 방법이 없다는 것이다. 특정 예외를 던져야만 성공하는 테스트가 있다고 해보자. 기대하는 예외타입을 테스트에 매개변수로 전달해야 하는 상황이다. 예외의 이름을 테스트 메스드 이름에 덧붙이..
38. 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라
대부분의 상항에서 열거 타입을 확장하는건 좋지 않은 생각이다. 기반 타입과 확장된타입들의 원소 모두를 순회할 방법도 마땅치 않다. 또, 확장성을 높이려면 고려할 요소가 늘어나 설계와 구현이 복잡해진다. 그런데, 확장할 수 있는 열거 타입이 어울리는 쓰임이 최소한 하나는 있다. 바로 연산 코드이다. 이따금 API가 제공하는 기본 연산 외에 사용자 확장 연산을 추가할 수 있도록 열어줘야 할 때가 있다. 기본 아이디어는 열거 타입이 임의의 인터페이스를 구현할 수 있다는 사실을 이용하는 것이다. public interface Operation { double apply(double x, double y); } public enum BasicOperation implements Operation { PLUS("+..
37. ordinal 인덱싱 대신 EnumMap을 사용하라
이따금 배열이나 리스트에서 원소를 꺼낼 때 ordinal 메서드로 인덱스를 얻는 코드가 있다. 이런 코드는 동작은 하지만 문제가 한가득이다. 배열은 제네릭과 호환되지 않으니 비검사 형변환을 수행해야 하고 깔끔히 컴파일되지 않는다. 배열은 각 인덱스의 의미를 모르니 출력 결과에 직접 레이블을 달아야 한다. 가장 심각한 문제는 정확한 정수값을 사용한다는 것을 개발자가 직접 보증해야 한다는 점이다. 정수는 열거 타입과 달리 타입 안전하지 않기 때문에 잘못된 동작이 발생할 수 있다. public class Plant { final String name; final LifeCycle lifeCycle; public Plant(String name, LifeCycle lifeCycle) { this.name = n..
35. ordinal 메서드 대신 인스턴스 필드를 사용하라
대부분의 열거 타입 상수는 자연스럽게 하나의 정숫값에 대응된다. 모든 열거 타입은 해당 상수가 그 열거 타입에서 몇 번째 위치인지 반환하는 ordinal이라는 메서드를 제공한다. 이런 이유로 열거 타입 상수와 연결된 정수값이 필요할 때 ordinal 메서드를 이용하고 싶은 유혹에 빠진다. 그러나 해당 메서드를 사용하면 유지보수가 끔찍해질 수 있다. 구현에 따라 코드가 오동작 할 수 있고, 값을 추가하거나 제거하기가 힘들어질 수 있다. 또한 중간에 값을 비워둘 수 없게되고 때문에 더미 상수를 추가해야 하는 상황이 올 수 있다. 해답은 간단하다. ordinal 메서드를 사용하지 말고 인스턴스 필드에 int 필드를 추가 후 저장하여 사용하면 된다. public enum Ensemble { SOLO(1), DU..
34. int 상수 대신 열거 타입을 사용하라
열거 타입은 일정 개수의 상수 값을 정의한 다음, 그 외의 값은 허용하지 않는 타입이다. 사계절, 태양계의 행성, 카드게임의 카드 종류 등이 좋은 예다. public static final int APPLE_FUJI = 0; public static final int APPLE_PIPPIN = 1; public static final int APPLE_SMITH = 2; public static final int ORANGE_NAVEL = 0; public static final int ORANGE_TEMPLE = 1; public static final int ORANGE_BLOOD = 2; 상수를 이용한 열거 패턴 기법에는 타입 안전을 보장할 방법이 없으며, 표현력도 좋지 않다. 정수 상수는 문자열로..
33. 타입 안정 이종 컨테이너를 고려하라
제네릭은 Set 등의 컬렉션과 ThreadLocal, AtomicReference 등의 단일원소 컨테이너에도 흔히 쓰인다. 이런 모든 쓰임에서 매개변수화되는 대상은 컨테이너 자신이다. 따라서 하나의 컨테이너에서 매개변수화할 수 있는 타입의 수가 제한된다. 하지만 더 유연한 수단이 필요할 때도 종종 있다. 데이터베이스의 행은 임의 개수의 열을 가질 수 있는데, 모두 열을 타입 안전하게 이용하려면? 컨테이너 대신 키를 매개변수화한 다음, 컨테이너에 값을 넣거나 뺄 때 매개변수화한 키를 함께 제공하면 된다. 이렇게 하면 제네릭 타입 시스템이 값의 타입이 키와 같음을 보장해줄 것이다. 이러한 설계 방식을 타입 안전 이종 컨테이너 패턴이라 한다. public class Favorite { private Map f..