Skip to content

Item 49. 매개변수가 유효한지 검사하라 #48

@byunghyunkim0

Description

@byunghyunkim0

Chapter : 8. 메서드

Item : 49. 매개변수가 유효한지 검사하라

Assignee : byunghyunkim0


🍑 서론

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

이런 제약은 반드시 문서화해야 하며 메서드 몸체가 시작되기 전에 검사해야한다.

🍑 본론

매개변수 검사에서 생기는 문제

  1. 메서드가 수행되는 중간에 모호한 예외를 던지며 실패할 수 있다.
  2. 메서드가 잘 수행되지만 잘못된 결과를 반환할 수 있다.
  3. 메서드는 문제없이 수행됐지만, 어떤 객체를 이상한 상태로 만들어놓아서 미래의 알 수 없는 시점에이 메서드와는 관련 없는 오류를 낼 수 있다.
  • 매개변수 검사에 실패하면 실패 원자성을 어기는 결과를 낳을 수 있다.(아이템 76)
    • 호출된 메서드가 실패해도 해당 객체는 메서드 호출 전 상태를 유지하는 특성을 실패 원자적이라고 한다

예외의 문서화

  • public과 protected 메서드는 매개변수 값이 잘못됐을 때 던지는 예외를 문서화해야 한다.
    • @throws 자바독 태그를 사용하면 된다.
    • 일반적으로 IllegalArgumentException, IndexOutOfBoundsException, NullPointerException 중 하나가 될 것이다.
    • 제약을 문서화한다면 어겼을 때 발생하는 예외도 함께 기술해야한다.

예외의 문서화 예시

// BigInteger 클래스에 구현되어있음
/**
 * 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));
}
  • @throws태그에 0보다 작거나 같은 값의 나머지를 구하려 하면, ArithmeticException이 발생한다고 알려주고 있다.

null 검사

  • 자바 7에 추가된 java.util.Objects.requireNonNull메서드는 유연하고 사용하기 편하니, 더 이상 null검사를 수동으로 하지 않아도 된다.
public class Main {
    public static void main(String[] args) {
        String str = "Hello, world!";
        printStringLength(str);  // 정상적인 문자열을 전달

        String nullStr = null;
        printStringLength(nullStr);  // null을 전달
    }

    public static void printStringLength(String str) {
        // java.util.Objects.requireNonNull을 사용하여 null 검사
        String input = java.util.Objects.requireNonNull(str, "문자열은 null일 수 없습니다.");

        // 문자열의 길이를 출력
        System.out.println("입력된 문자열의 길이: " + input.length());
    }
}
  • 필요한 곳 어디서든 순수한 null 검사 목적으로 사용해도된다.

assert

  • public이 아닌 메서드라면 단언문(assert)을 사용해 매개변수 유효성을 검증할 수 있다.
public class Main {
    public static void main(String[] args) {
//        long[] arr = {5, 2, 7, 3, 9, 1};
//        int offset = 2;
//        int length = 4;

        long[] arr = null;
        int offset = -1;
        int length = 5;

        sort(arr, offset, length);

        for (int i = offset; i < offset + length; i++) {
            System.out.print(arr[i] + " ");
        }
    }

    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;
        for (int i = offset; i < offset + length - 1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < offset + length; j++) {
                if (a[j] < a[minIndex]) {
                    minIndex = j;
                }
            }
            long temp = a[minIndex];
            a[minIndex] = a[i];
            a[i] = temp;
        }
    }
}
  • assert에서 실패하면 AssertionError이 발생한다.
  • 런타임에 아무런 효과도, 성능 저하도 없다. (debugging할때 사용)
  • java를 실행할 때 명령줄에서 -ea 혹은 --enableassertions 플래그를 설정해서 확인할수있다.
javac Main.java
java -ea Main.java

나중에 쓰려고 저장하는 매개변수의 유효성을 검사하라

static List<Integer> intArrayAsList(int[] a){
    Objects.requireNonNull(a);

    ...
}
  • Objects.requiereNonNull을 이용해 null검사를 수행한다.
  • null 검사를 생략했다면 나중에 배열을 사용하려고 할때 NullPointerException이 발생한다.
    • 배열을 어디서 가져왔는지 추적하기 어려워 디버깅이 상당히 괴로워질수 있다.

유효성 검사 예외

  • 유효성 검사 비용이 지나치게 높거나, 실용적이지 않을 때
  • 계산 과정에서 암묵적으로 검사가 수행될 때
    • Collections.sort(List)는 정렬을 수행할 때 과정에서 상호 비교 될 수 없는 타입이 들어있다면 ClassCastException을 던진다.
    • 비교하기 앞서 모든 객체가 상호 비교될 수 있는지 검사할 필요가 없다.
    • 암묵적 유효성 검사에 너무 의존했다가는 실패 원자성을 해칠수 있으니 주의하자
  • 유호성 검사가 실패했을 때 잘못된 예외를 던지기도 한다.
    • API문서에서 던지기로 한 예외가 다를 수 있다.
    • 예외 번역(exception translate) 관용구를 사용하여 API 문서에 기재된 예외로 번역해줘야 한다.

🍑 결론

  • "매개변수에 제약을 두는 게 좋다"라고 해석하지 마라
  • 메서드는 최대한 범용적으로 설계해야 함
  • 매개변수들에 어떤 제약이 있을지 생각해고 문서화하고 명시적으로 검사해라

Referenced by

https://blueyikim.tistory.com/2452

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions