메소드와 생성자 대부분은 입력 매개변수의 값이 특정 조건을 만족하길 바란다

예를 들어

  • 인덱스 값은 음수이면 안된다.
  • 객체 참조는 null이 아니어야 한다

하는 식이다.

 

 

이런 제약은 반드시 문서화해야 한다.

메소드 몸체가 실행되기 전에 매개변수를 확인한다면 잘못도니 값이 넘어왔을 때

즉각적이고 깔끔한 방식으로 예외를 던질 수 있다. (유효성 검사를 한다는 의미인것 같다.)

 

 

 

매개변수 검사를 제대로 하지 못하면 몇가지 문제가 생긴다

  • 메소드가 수행되는 중간에 모호한 예외를 던지며 실패할 수 있다.
  • 더 나쁜 상황은 메소드가 잘 수행되지만 잘못된 결과를 반환할 때다.
  • 매개변수 검사에 실패하면 실패 원자성을 어기는 결과를 낳을 수 있다.
    • 실패 원자성 : 호출에 실패하더라도 객체는 호출 전 결과를 유지해야 함

 

 

public과 protected 메소드는 매개변수 값이 잘못됐을 때 던지는 예외를 문서화 해야한다.

@throws 자바독 태그를 사용함녀 된다.

보통은 IlleagalArgumentException, IndexOutOfBoundsException, NullPointerException 중 하나다.

 

 

매개변수 제약을 어겼을 때의 예시도 서술해줘야하는데 다음은 예시다

/**
 * Returns a BigInteger whose value is {@code (this mod m}).  This method
 * differs from {@code remainder} in that it always returns a
 * <i>non-negative</i> BigInteger.
 *
 * @param  m the modulus.
 * @return {@code this mod m}
 * @throws ArithmeticException {@code m} &le; 0
 * @see    #remainder
 */
public BigInteger mod(BigInteger m) {
    if (m.signum <= 0)
        throw new ArithmeticException("BigInteger: modulus not positive");

    BigInteger result = this.remainder(m);
    return (result.signum >= 0 ? result : result.add(m));
}}
  • 이 메소드는 m이 null이면 m.signum 호출 때 NullPointerException을 던진다.
  • 그러나 위 예시에선 확인할 수 없는데 이 설명을 BigInteger 클래스 수준에서 기술했기 때문이다.
  • 클래스 수준 주석은 그 클래스의 모든 public 메소드에 적용되므로 각 메소드에 일일이 기술하는 것보다 훨씬 깔끔한 방법이다.

 

 

공개되지 않은 메소드의 경우

공개되지 않은 메소드라면 패키지 제작자가 메소드가 호출되는 상황을 통제할 수 있다.

따라서 유효한 값만이 메소드에 넘겨지리라는 것을 우리가 보증할 수 있고 그렇게 해야한다.

//재귀 정렬용 private 도우미 함수
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;
    ... // 계산 수행
}
  • 여기서 핵심은 이 단언문들은 자신이 단언한 조건이 무조건 참이라고 선언한다는 것
  • 단언문과 일반적인 유효성 검사가 다른점
    • 실패하면 AssertionError를 던짐
    • 런타임에 아무런 효과도 아무런 성능저하도 없음

 

 

정리

  • 메소드나 생성자를 작성할 때 매개변수의 제약에 대해 생각해야함
  • 제약들을 문서화하고 메소드 코드 시작부분에 명시적으로 검사해야 함

+ Recent posts