요약
•
오류는 가급적 발생한 지점에서 찾을 수 있게 매개변수로 받은 값들에 대해 로직 수행 전 미리 검사하자.
메서드 몸체가 실행되기 전에 매개변수를 검사하자
•
인덱스는 음수가 되면 안된다거나 객체 참조는 null이 아니어야 한다는 것과 같은 제약들은 반드시 문서화 되어야하며 메서드 몸체가 시작되기 전에 검사해야한다.
•
이는 오류는 가능한 빨리 발생한 지점에서 잡아야한다는 일반 원칙 중 한 사례이기도 하며, 오류가 발생한 즉시 못 잡으면 오류를 감지하기도 어렵고 감지해도 발생지점을 찾기 어려워진다.
•
매개변수 검사를 메서드 몸체 시작 전에 하지 않는다면, 메서드 수행 중 간에 모호한 예외를 던지며 실패할 수 있거나 메서드가 수행되었지만 잘못된 결과를 반환해 미래의 알 수 없는 시점에 오류가 발생하도록 만들 수 있다.
•
생성자를 호출할 때에도 매개변수의 유효성을 검사하자. 생성자 매개변수의 유효성 검사는 클래스 불변식을 어기는 객체가 만들어지지 않게 하는데 꼭 필요하다.
공개 API의 매개변수 검사가 잘못되는 경우는 문서화하자
•
public과 protected 메서드는 매개변수가 잘못되는 경우에 던지는 예외들에 대해 문서화 해야한다.
•
매개변수의 제약을 문서화한다면 당연히 매개변수의 제약을 위반했을 때의 예외도 문서화 되어야한다.
/*
* @param m 계수
* ...
* @throws ArithmeticException m이 0보다 작거나 같으면 발생한다.
*/
public BingInteger mod(BigInteger m) {
if (m.signum() <= 0)
throw new ArithmeticException("계수(m)는 양수이어야 합니다. " + m);
...
}
Java
복사
•
위 코드는 m 값에 null이 들어가면 NullPointerException이 발생하지만, 문서화 내용에는 해당 사항이 들어있지 않다.
•
문서화 내용에 해당 사항을 추가하고, 자바 7에 추가된 java.util.objects.requireNonNull 메서드를 사용하여 null에 대한 예외를 던져야 한다.
/*
* @param m 계수
* ...
* @throws NullPointerException m이 null인 경우 발생한다.
* @throws ArithmeticException m이 0보다 작거나 같으면 발생한다.
*/
public BingInteger mod(BigInteger m) {
BigInteger tmp = Objects.requrieNonNull(m, "계수(m)는 null이 아니어야 합니다.");
if (tmp.signum() <= 0)
throw new ArithmeticException("계수(m)는 양수이어야 합니다. " + m);
...
}
Java
복사
단언문(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;
...
}
Java
복사
•
만약 public이 아닌 메서드라면 이와 같이 단언문(assert)를 사용해 매개변수 유효성을 검증할 수 있다.
•
단언문은 선언된 조건이 무조건 참이라고 선언하는 것으로, 해당 조건을 만족하지 못하면 AssertionError를 던진다.
•
또한 단언문은 런타임에는 아무런 효과도 없고 성능 저하도 없다.
매개변수 유효성 검사의 예외
•
유효성 검사 비용이 지나치게 높거나 실용적이지 않을 때, 혹은 계산 과정에서 암묵적으로 검사가 수행될 때에는 매개 변수 유효성 검사를 하지 않는다.
•
Collections.sort(List)의 경우에는 리스트 안의 객체가 서로 상호 비교될 수 있어야 하는데, 상호 비교될 수 없는 타입의 객체가 들어 있다면 ClassCastException이 발생할 것이기 때문에 따로 검사하지 않아도 된다.
•
하지만 이러한 암묵적 유효성 검사에 너무 의존하다가는 실패 원자성을 해칠 수 있으니 주의하자.