규칙 38 인자의 유효성을 검사하라
- 인자 유효성을 검사하지 않으면 생기는 문제
- 처리 도중에 이상한 예외를 내면서 죽어버리는 것
- 실행이 제대로 되는 것 같기는 한데 잘못된 결과가 나오는 것
- 클래스 불변식(invariant)을 위반하는 객체 가 만들어지는 것을 막으려면, 생성자에 전달되는 인자의 유효성을 반드시 검사해야 한다.
- 확증문을 클라이언트가 패키지를 어떻게 이용하건 확증 조건(asserted condition)은 항상 참이 되어야 한다고 주장하는 것이다.
- 유효성 검사 예외
- 오버헤드가 너무 크거나 비현실적이고, 계산과정에서 유효성 검사가 자연스럽게 이루어지는 경우
- 암묵적인 유효성 검사 방법에 지나치게 기대다 보면, 실패 원자성(failure atomicity)을 잃게 된다는 점이다
규칙 39 필요하다면 방어적 복사본을 만들라
- 여러분이 만드는 클래스의 클라이언트가 불변식(invariant)을 망가뜨리기 위해 최선을 다할 것이라는 가정하에, 방어적으로 프로그래밍해야 한다.
- 생성자로 전달되는 변경 가능 객체를 반드시 방어적으로 복사
- 인자의 유효성을 검사하기 전에(규칙 38) 방어적 복사본을 만들었다는 것에 유의하자. 유효성 검사는 복사본에 대해서 시행한다.
- 취약 구간(window of vulnerability)
- 보안 커뮤니티에서는 이런 공격을 TICTOU 공격, 즉 time-of-check/time-of-use 공격
- Date 클래스는 final 클래스가 아니므로, clone 메서드가 반드시 java.util.Date 객체를 반환할 거라는 보장이 없다.
- 인자로 전달된 객체의 자료형이 제3자가 계승할 수 있는 자료형일 경우, 방어적 복사본을 만들 때 clone을 사용하지 않도록 해야 한다.
- 변경 기능 내부 필드에 대한 방어적 복사본을 반환하도 록 접근자를 수정해야 한다.
규칙 40 메서드 시그너처는 신중하게 설계하라
- 메서드 이름은 신중하게 고르라.
- 모든 이름은 표준 작명 관습(standard naming conversion)을 따라야 한다.
- 좀 더 널리 합의된 사항에도 부합하는 이름을 고르는 것이다.
- 편의 메서드(convenience method)를 제공하는 데 너무 열 올리지 마라.
- “맡은 일이 명확하고 거기 충실해야(pull its weight)” 한다.
- 클래스나 인터페이스가 수행해야 하는 동작 각각에 대해서 기능적으로 완전한 메서드를 제공하라.
- 인자 리스트(parameter list)를 길게 만들지 마라.
- 자료형이 같은 인자들이 길게 연결된 인자 리스트는 특히 더 위험하다.
- 긴 인자 리스트를 짧게 줄이는 방법
- 여러 메서드로 나누는 것
- 도움 클래스(helper class)를 만들어 인자들을 그룹별로 나누는 것
- 빌더 패턴(builder pattern)을 고쳐서 객체 생성 대신 메서드 호출에 적용하는 것
- 인자의 자료형으로는 클래스보다 인터페이스가 좋다.
- 인자 자료형으로 boolean을 쓰는 것보다는, 원소가 2개인 enum 자료형을 쓰는 것이 낫다.
규칙 41 오버로딩할 때는 주의하라
- 오버로딩된 메서드 가운데 어떤 것이 호출될지는 컴파일 시점에 결정되기 때문이다.
- 오버로딩된 메서드는 정적 (static)으로 선택되지만, 재정의된 메서드는 동적(dynamic)으로 선택되기 때문이다.
- 재정의된(Overridden) 메서드의 경우, 선택 기준은 메서드 호출 대상 객체의 자료형이다.
- 오버로딩을 사용할 때는 혼란스럽지 않게 사용할 수 있도록 주의해야 한다.
- 혼란을 피하는 안전하고 보수적인 전략은, 같은 수의 인자를 갖는 두 개의 오버로딩 메서드를 API에 포함시키지 않는 것이다.
- 생성자에는 다른 이름을 사용할 수 없다. 생성자가 많다면, 그 생성자들은 향상 오버로딩된다.
- 생성자 대신 정적 팩터리 메서드를 사용하는 옵션을 시용할 수도 있다.
- E와 int는 더 이상 완전히 다르다고 말할 수 없게 되었다.
public class SetList {
public static void main(String[] args) {
Object o = new char[]{123};
char chars[] = new char[]{123};
System.out.println(String.valueOf(chars));
System.out.println(String.valueOf(o));
Set<Integer> set = new TreeSet<>();
List<Integer> list = new ArrayList<>();
// List
// boolean remove(Object o);
// E remove(int index);
// Set
// boolean remove(Object o);
for (int i = -3; i < 3; i++) {
set.add(i);
list.add(i);
}
for (int i = 0; i < 3; i++) {
set.remove(i);
list.remove(i);
}
System.out.println(set + " " + list);
}
}
규칙 42 varargs는 신중히 사용하라
- 자바 15부터는 공식적으로는 가변 인자 메서드(variable arity method)라고 부르는 varargs 메서드가 추가
- 마지막 인자가 배열이라고 해서 무조건 뜯어고칠 생각은 버려라.
- varargs는 정말로 임의 개수의 인자를 처리할 수있는 메서드를 만들어야 할 때만 사용하라.
- varargs가 추가된 것은 자바 1.5부터 플랫폼에 추가된 printf 메서드와, varargs를 이용할 수 있도록 개선된 핵심 리플렉션(core reflection) 기능 때문이다.
- 마지막 인자가 배열이라고 해서 무조건 뜯어고칠 생각은 버려라.
- varargs는 정말로 임의 개수의 인자를 처리할 수있는 메서드를 만들어야 할 때만 사용하라.
규칙 43 null 대신 빈 배열이나 컬렉션을 반환하라
// 컬렉션에서 배열울 만둘어 반환하는 올바른 방법
private final List<Cheese> cheeseslnStock = ...;
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[O];
*
* @return 재고가 남은 모든 치즈 목록을 배열로 만들어 반환
*/
public Cheese[] getCheeses()
return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}
- Collection.toArray(T[])의 명세를 보면, 인자로 주어진 배열이 컬렉션의 모든 원소를 담을 정도로 큰 경우에는 해당 배열을 반환값으로 시용한다고 되어 있다. 따라서 위의 숙어대로 하면 빈 배열은 절대로 자동 할당되지 않는다.
- null 대신에 빈 배열이나 빈 컬렉션을 반환하라는 것이다.
규칙 44 모든 API 요소에 문서화 주석을 달라
- 좋은 API 문서를 만들려면 API에 포함된 모든 클래스, 인터페이스, 생성자, 메서드, 그리고 필드 선언에 문서화 주석을 달아야 한다.
- 메서드에 대한 문서화 주석은 메서드와 클라이언트 사이의 규약(contract)을 간명하게 설명해야 한다.
- 문서화 주석에는 해당 메서드의 모든 선행츠건 (precondition)과 후행조건(postcondition)을 나열해야 한다.
- 선행조건과 후행조건 의에도, 메서드는 부작용(side effect)에 대해서도 문서화 해야 한다.
- 더 이상은 HTML <code>나 <tt>를 사용할 필요가 없다. Javadoc의 {@code} 태그를 쓰는 편이 더 낫다. HTML의 메타문자를 이스케이프 처리할 필요가 없기 때문이다.
- 메서드에 대한 문서화 주석은 메서드와 클라이언트 사이의 규약(contract)을 간명하게 설명해야 한다.
- 제네릭 자료형이나 메서드에 주석을 달 때는 모든 자료형 인자들을 설명해야 한다.
- enum 자료형에 주석을 달 때는 자료형이나 public 메서드뿐 아니라 상수 각각에도 주석을 달아 주어야 한다.
- 어노테이션 자료형에 주석을 달 때는 자료형뿐 아니라 모든 멤버에도 주석을 달아야 한다.
- 문서화 주석을 제대로 만들려면 오라클의 How to Write Doc Comments Javadoc-guide를 꼭 읽어야 한다.