1. 오류는 가능한 한 빨리 잡자
오류를 발생한 즉시 잡지 못하면 오류를 감지하기 어렵고, 지점을 찾기 어려워진다. 예를 들어 인덱스 값은 음수이면 안되며, 객체 참조는 null 이면 안된다 와 같은 특정 조건을 메서드와 생성자의 매개변수에 바라는 경우가 있다. 메서드 본체로 넘어가기 전, 매개변수를 확인해야 깔끔한 방식으로 예외를 던질 수 있다.
public 과 protected 메서드는 매개변수 값이 잘못됐을 때 던지는 예외를 문서화하자
@throws 자바독 태그를 사용하자. IllegalArgumentException, IndexOutOfBoundException, NullPointerException 등이 될 것이다. 이를 문서화하고 제약을 어겼을 때 발생하는 예외도 기술하자. 전형적인 예시를 살펴보자.
/**
* (현재 값 mod m) 값을 반환한다. 이 메서드는
* 항상 음이 아닌 BigInteger를 반환한다는 점에서 remainder 메서드와 다르다.
*
* @param m 계수(양수여야 한다)
* @return 현재 값 mod m
* @throws ArithmeticException m이 0보다 작거나 같으면 발생한다.
*/
public BigInteger mod(BigInteger m) {
if (m.signum() <= 0) {
throw new ArithmeticException("계수(m)는 양수여야 합니다. " + m);
... // 계산 수행
}
이 메서드는 m 이 null 이면, m.signum() 호출 때 NullPointerException 을 던진다. 이는 메서드가 아닌 BigInteger 클래스 수준에서 기술되었다. 클래스 수준에서 기술할 수 있다면 훨씬 깔끔한 방법이 될 수 있다.
2. 자바에 추가된 검사 기능
자바 7에서는 java.util.Objects.requireNonNull 메서드를 통해 null 검사를 수동으로 하지 않아도 되었다. 예외 메세지도 지정할 수 있다. 또한 입력을 그대로 반환하기도 하여 값을 사용함과 동시에 null 검사를 수행할 수 있다.
this.strategy = Objects.requiredNonNull(strategy, "전략");
자바 9에서는 Objects 에 범위 검사도 가능해졌다. checkFromIndexSize, checkFromToIndex, checkIndex 라는 메서드들은 리스트와 배열 전용으로 설계되어 유연하지는 않다. 그래도 유용하게 사용될 수 있다.
3. private 메서드의 매개변수 검증
public 이 아닌 메서드라면 단언문(assert) 을 사용해 매개변수 유효성을 체크하자. 아래 예시를 살펴보자.
private static void sort(long a[], int offset, int length) {
assert a != null;
assert offset >= 0 && offset <= a.length;
assert length >= 0 && length <= a.length = offset;
... // 계산 수행
}
assert 를 통해 선언된 조건이 무조건 참이라고 선언해버린다. 실패하면, AssertionError 를 던진다.

그리고, 런타임에 아무런 효과도, 아무런 성능 저하도 없다. 참고로, 메서드가 직접 사용하지는 않으나 나중에 쓰기 위해 저장하는 매개변수는 특히 더 신경쓰자. 나중에 메서드로부터 반환받고 사용하려 할 때 에러가 발생한다면, 디버깅이 괴로워질 수 있다. 추가로,생성자 매개변수의 유효성 검사는 클래스 불변식을 어기는 객체가 만들어지지 않게 하는데 꼭 필요하다. 예를 들어, final 필드를 사용하는 클래스에서 불변성을 유지하려면, 생성자에서 상태를 설정한 후 해당 필드를 변경할 수 없게 해야 한다.
4. 예외
메서드 몸체 실행 전에 매개변수 유효성 검사를 한다는 규칙에도 물론 예외는 있다. 유효성 검사 비용이 지나치게 높거나, 실용적이지 않거나, 계산 과정에서 암묵적으로 검사가 수행될 수 있다. 예를 들어, Collection.sort(List) 메서드를 생각해보면 원소들이 모두 상호 비교될 수 있어야 한다. 만약 비교될 수 없는 타입이 들어가 있다면 비교할 때 이미 ClassCastException 을 던질 것이다. 미리 손수 비교할 필요가 없다는 것이다.
결론은 매개변수에 제약을 두는 게 좋다는게 아니라, 최대한 범용적으로 설계하자. 제약이 적을수록 좋을 수 있다.
'1️⃣ 백앤드 > 이펙티브 자바' 카테고리의 다른 글
| [아이템51] 메서드 시그니처를 신중히 설계하라 (0) | 2024.11.24 |
|---|---|
| [아이템50] 적시에 방어적 복사본을 만들라 (0) | 2024.11.21 |
| [아이템48] 스트림 병렬화는 주의해서 적용하라 (0) | 2024.11.20 |
| [아이템47] 반환 타입으로는 스트림보다 컬렉션이 낫다 (0) | 2024.11.19 |
| [아이템46] 스트림에서는 부작용 없는 함수를 사용하라 (0) | 2024.11.18 |