티스토리 뷰

728x90
반응형

매개변수가 유효한지 검사하라


  • 메서드와 생성자 대부분은 입력 매개변수의 값이 특정 조건을 만족하기를 바랍니다. 예를 들어 인덱스의 값은 음수여서는 안되고, 객체 참조는 NULL이어서는 안됩니다. 이런식의 제약은 반드시 문서화해야하며, 메서드 몸체가 시작되기 전에 검사해야 합니다.
  • 오류는 가능한 한 빨리 잡아야하며, 오류를 발생한 즉시 잡지 못하면 해당 오류를 감지하기 어려워지고, 감지하더라도 오류의 발생 지점을 찾기 어려워집니다.

 

💡 매개변수 검사를 제대로 하지 않은 경우

  • 메서드가 수행되는 중간에 모하한 예외를 던지며 실패할 수 있습니다.
  • 더 나쁜 상황은 메서드가 잘 수행되지만 잘못된 결과를 반환하는 상황이 발생할 수도 있습니다.
  • 더 좋지 않은 상황은 메서드는 문제없이 수행됐지만 어떤 객체를 이상한 상태로 만들어 놓아서 미래에 알 수 없는 시점에 이 메서드와는 관련없는 오류를 낼 때입니다.
  • 다시 말해 매개변수 검사에 실패하면 실패 원자성을 어기는 결과를 초래할 수 있습니다. 실패 원자성이란, 객체가 메서드 호출해 실패하더라도 상태가 이전과 동일해야 한다는 것입니다.

 

💡 public과 protected 메서드는 문서화하라

  • 매개변수의 제약을 문서화하고 이 제약을 어겼을 때 발생할 수 있는 예외를 명시하면 해당 API를 사용하는 개발자가 해당 제약을 지킬 확률을 높일 수 있습니다.
  • 아래에서는 NPE에 대한 내용은 기술되어 있지 않지만, BigInteger 클래스 수준에서 기술하였기 때문에 이 클래스의 public 메서드 전체에 적용되기에 각 메서드에서 기술할 필요가 없습니다.
/**
 * (현재 값 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);
    }
    //... 
}

 

💡 Objects.requireNonNull

  • 자바 7에 추가된 Objects.requireNonNull 메서드는 null 검사를 자동으로 해줍니다. 내부적으로는 아래처럼 되어 있으며, null인 경우를 체크해주고 null이 아니라면 값을 그대로 반환합니다.
public class Example {

    public static void main(String[] args) {
        String s = null;
        s = Objects.requireNonNull(s, "해당 문자열은 NULL입니다.");

        System.out.println(s);
    }
}

public class Objects {

    // ...
    public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

    // ...

    public static <T> T requireNonNull(T obj, String message) {
        if (obj == null)
            throw new NullPointerException(message);
        return obj;
    }
}

 

💡 단언문(assert)를 사용한 매개변수 유효성 검증

  • 공개되지 않은 메서드(private)라면 메서드가 호출되는 상황을 통제할 수 있습니다. 즉 public이 아닌 메서드라면 단언문을 사용해 매개변수 유효성을 검증할 수 있습니다.
  • 이 단언문들은 자신이 단언한 조건이 무조건 참이라고 선언합니다. 만약 참이 아닌 경우 AssertionError가 발생합니다.
  • 단언문은 일반적인 유효성 검사와 다릅니다.
  • 다른점 1 - 실패하면 무조건 AssertionError가 발생합니다.
  • 다른점 2 - 런타임에는 아무런 효과도 성능 저하도 없습니다. 단 java를 싱핼할 때 명령줄에서 --ea 혹은 --enablessertions 플래그를 설정하면 런타임에도 영향을 미칩니다.
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;
    // 계산 수행
}

 

💡 매개변수 유효성검사 예외상황

  • 항상 로직 최상단에서 매개변수 유효성 검사를 해야하는것만은 아닙니다.
  • 유효성 검사 비용이 너무 높거나 실용적이지 않거나, 계산 과정에서 암묵적으로 검사가 수행될 때는 예외상황으로 둘 수 있습니다.

 

💡 매개변수 유효성검사 예외상황에 대한 예시

  • List를 정렬하는데 Collections.sort() 메서드 같은 경우 모든 원소가 상호 비교될 수 있어야 정렬을 할 수 있는데, 상호 비교할 수 없는 경우 ClassCastException을 던집니다. 그렇기에 이런 경우는 비교하기전에 모든 객체의 상호 비교성을 검사할 필요가 없습니다.

 

✔️ 정리

  • 일반적으로 로직의 최상단에 매개변수에 대한 유효성 검사 로직을 넣는게 좋습니다. 이를 놓칠 경우 잘못된 값으로 인해 오류를 감지하는게 어려워지고, 더욱 심각한 상황이되면 오류의 발생 지점을 찾기 어려워집니다.

 

 

 

728x90
반응형