매개변수가 유효한지 검사하라
- 메서드나 생성자를 작성할 때 매개변수들에 어떤 제약이 있을지 생각하고 메서드 코드 시작 부분에서 명시적으로 검사해야 한다.
- public과 protected 메서드는 매개변수 값이 잘못됐을 때 던지는 예외를 문서화해야 한다.
- 자바 7에 추가된 java.util.Objects.requireNonNull 메서드는 유연하고 사용하기도 편하니, 더 이상 null 검사를 수동으로 하지 않아도 된다.
this.stratergy = Objects.requireNonNull(strategy, "전략");
적시에 방어적 복사본을 만들라
- 클라이언트에서 불변식을 깨뜨리려고 한다고 가정하고 방어적으로 프로그래밍해야 한다.
- Date는 낡은 API이니 새로운 코드를 작성할 때는 더 이상 사용하면 안된다. (LocalDateTime이나 ZonedDateTime을 사용하라.)
- 생성자에서 받은 가변 매개변수 각각을 방어적으로 복사해야 한다.
- 매개변수가 제3자에 의해 확장될 수 있는 타입이라면 방어적 복사본을 만들 때 clone을 사용해서는 안 된다.
메서드 시그니처를 신중히 설계하라
- 메서드 이름을 신중히 짓자.
- 편의 메서드를 너무 많이 만들지 말자. (확신이 서지 않으면 만들지 말자.)
- 매개변수 목록은 짧게 유지하자. (같은 타입의 매개변수 여러개가 연달아 나오는 경우가 특히 해롭다.)
- 매개변수의 타입으로는 클래스보다는 인터페이스가 더 낫다.
- boolean보다는 원소 2개짜리 열거 타입이 낫다.
다중정의는 신중히 사용하라
- 오버라이드한 메서드는 동적(런타임)으로 선택되고, 오버로드한 메서드는 정적(컴파일타임)으로 선택 된다.
- 아래의 main 함수를 수행하면 "컬렉션" 만 세 번 출력 된다. (런타임에는 타입이 달라지지만 컴파일타임(for 문)에는 c는 항상 Cillection<?> 타입이다.)
public class ConllectionClassifier {
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) {
Collection<?>[] collections = {
new HashSet<String>(),
new ArrayList<String>(),
new HashMap<String, String>().values()
};
for (Collection<?> c : collections) {
System.out.println(classify(c));
}
}
}
- 아래의 main 함수를 수행하면 "와인", "스파클링 와인", "샴페인" 이 출력 된다.
Class Wine {
String name() { return "와인"; }
}
Class SparklingWine extends Wine {
@Override String name() { return "스파클링 와인"; }
}
Class Champagne extends SparklingWine {
@Override String name() { return "샴페인"; }
}
public class Overriding {
public static void main(String[] args) {
List<Wine> wineList = List.of(new Wine(), new SparklingWine(), new Champagne());
for (Wine wine : wineList) {
System.out.println(wine.name());
}
}
}
- 안전하고 보수적으로 가려면 매개변수 수가 같은 오버로딩한 메서드는 만들지 말자.
- 메서드를 오버로딩할 때, 서로 다른 함수형 인터페이스라도 같은 위치의 인수로 받아서는 안 된다.
가변인수는 신중히 사용하라
- 메서드를 정의할 때 필수 매개변수는 가변인수 앞에 두자.
- 필수 매개변수를 포함하는 가변인수 (X)
static int min(int... args) {
if (args.length == 0) {
throw new IllegalArgumentException("인수가 1개 이상 필요합니다.");
}
int min = args[0];
...
}
- 가변인수 앞에 필수 매개변수를 사용 (O)
static int min(int firstArg, int... args) {
int min = firstArg;
...
}
- 가변인수를 사용할 때는 성능 문제까지 고려해야 한다. (가변인수는 배열을 생성해서 매개변수를 담는다.)
- ex) 매개변수가 3개 이상인 경우에만 가변인수를 사용
public void foo() { }
public void foo(int a1) { }
public void foo(int a1, int a2) { }
public void foo(int a1, int a2, int... rest) { }
null이 아닌, 빈 컬렉션이나 배열을 반환하라
- null을 반환하는 API는 사용하기 어렵고 오류 처리 코드도 늘어난다. 그렇다고 성능이 좋은 것도 아니다.
Optional 반환은 신중히 하라
- Optional을 반환하는 메서드는 절대 null을 반환하지 말자.
- Collection, Stream, Array, Optional 같은 컨테이너 타입은 Optional로 감싸면 안 된다.
- 박싱된 기본 타입을 담은 Optional을 반환하지 말자. (OptionalInt, OptionalLong ... 등을 사용하자.)
- Optional을 컬렉션의 키, 값, 원소나 배열의 원소로 사용하지 말자. (반환값 이외의 용도로 쓰는 경우는 매우 드물다.)
- Optional 반환은 성능 저하가 뒤따르니, 성능에 민감한 메서드라면 null이나 예외를 던지는 편이 나을 수 있다.
공개된 API 요소에는 항상 문서화 주석을 작성하라
- 공개된 모든 클래스, 인터페이스, 메서드, 필드 선언에 문서화 주석을 달아야 한다.
- 메서드용 문서화 주석에는 해당 메서드와 클라이언트 사이의 규약을 명료하게 기술해야 한다.
- 한 클래스(혹은 인터페이스) 안에서 요약 설명이 똑같은 멤버(혹은 생성자)가 둘 이상이면 안 된다. (특히 오버로딩한 메서드가 있다면 특히 더 조심하자.)
- 제네릭 타입이나 제네릭 메서드를 문서화할 때는 모든 타입 매개변수에 주석을 달아야 한다.
- 열거 타입을 문서화할 때는 상수들에도 주석을 달아야 한다.
- 애너테이션 타입을 문서화할 때는 멤버들에도 모두 주석을 달아야 한다.
- 클래스 혹은 정적 메서드가 스레드 안전하든 그렇지 않든, 스레드 안전 수준을 반드시 API 설명에 포함해야 한다.
'책 > Effective Java' 카테고리의 다른 글
예외 (0) | 2020.03.27 |
---|---|
일반적인 프로그래밍 원칙 (0) | 2020.03.26 |
람다와 스트림 (0) | 2020.03.20 |
열거 타입과 애너테이션 (0) | 2020.03.18 |
제네릭 (0) | 2020.03.17 |