본문 바로가기
☕️ JAVA/🦋 Effective Java

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

by kukim 2022. 3. 21.

이 글은 책 이펙티브 자바 3판 Item 49와 하단 Reference 참고했습니다. 잘못된 내용이 있다면 편하게 말씀해주세요 🙏🏻


메서드, 생성자들의 입력값이 특정 조건을 만족하길 바란다. 예를 들어 인덱스 값은 음수 X, 객체 참조는 null이 아니어야 한다. 보통 메서드, 생성자의 몸체가 실행되기 전에 매개 변수 확인한다면 잘못된 값이 넘어왔을 때 즉각적, 깔끔한 방식으로 예외를 던질 수 있다. 어떻게 유효한지 검사해야 할까?

 

public, protected 메서드

  • 보통, 몸체(주요 로직) 실행되기 전 상단에서 매개변수 값 확인 후 예외를 던진다
  • 자바독에 @throws, 예외 경우를 작성한다.

public 또는 protected 메서드는 패키지 개발자가 아닌 외부 사용자가 사용한다. 따라서 매개변수 값이 잘못됐을 때 예외를 던진다. 

던지는 예외를 문서화(@throws 자바독 태그)한다. 문서를 통해 API 사용자가 제약을 지킬 가능성을 높인다.

아래 예는 BigInteger클래스의 생성자 중 하나로 자바독 @throws를 통해 어떤 경우에 예외가 발생하는지 명시하고 있고 실제로 생성자 몸체가 실행되기 전 상단에서 throw new NumberFormatException을 던지고 있다.

public class BigInteger extends Number implements Comparable<BigInteger> {
/**
// 생략
     * @param  val byte array containing a sub-array which is the big-endian
     *         two's-complement binary representation of a BigInteger.
     * @param  off the start offset of the binary representation.
     * @param  len the number of bytes to use.
     * @throws NumberFormatException {@code val} is zero bytes long.
     * @throws IndexOutOfBoundsException if the provided array offset and
     *         length would cause an index into the byte array to be
     *         negative or greater than or equal to the array length.
     * @since 9
     */
    public BigInteger(byte[] val, int off, int len) {
        if (val.length == 0) {
            throw new NumberFormatException("Zero length BigInteger");
        }
        Objects.checkFromIndexSize(off, len, val.length);

        if (val[off] < 0) {
            mag = makePositive(val, off, len);
            signum = -1;
        } else {
            mag = stripLeadingZeroBytes(val, off, len);
            signum = (mag.length == 0 ? 0 : 1);
        }
        if (mag.length >= MAX_MAG_LENGTH) {
            checkRange();
        }
    }

}

null 검사

if 조건문으로 null 검사도 좋지만 자바 7에 추가된 java.util.Objects.requireNonNull 메서드를 사용해 편리하게 null 검사를 할 수 있다. (requireNonNull 메서드 내부는 if 조건문과 동일하지만.. 가독성이 높다는 걸까)

public class testParameter {
    
    private void append(String str) {
        if (str == null) {
            throw new NullPointerException("매개변수 문자열이 null 입니다.");
        }

        // 코드
    }

    private void append(String str) {
        Objects.requireNonNull(str, "매개변수 문자열이 null입니다.");
        // 코드
    }
}
public final class Objects {

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

private 메서드인 경우

공개되지 않은 메서드라면 패키지 개발자가 메서드 호출되는 상황을 통제할 수 있다. 따라서 유효한 값만이 메서드에 넘겨지리라는 것을 스스로 보장할 수 있고, 그렇게 만들어야 한다. 따라서 public이 아닌 메서드라면 assert을 사용해 매개변수 유효성을 검증할 수 있다. assert 키워드는 일반 유효성 검사와 두 가지가 다르다. (참고 : Using Java Assertions - Baeldung)

  1. 실패하면 AssertionError를 던진다.
  2. 런타임에 실행되지 않는다.(성능 저하 없음, java 실행 시 옵션으로 -ea (—enableassertions) 플래그 설정하면 작동)

보통 개발 중에 테스트 목적으로 사용된다.


+a) 꼭 메서드 로직(몸체) 실행 전 매개변수 유효성 검사를 해야 할까?

- 유효성 검사 비용이 높거나 실용적이지 않을 때는 다른 방법을 고려하자. (예외 던지는 대신 다른 방법을 두어야겠다.)

- 계산 과정에서 암묵적으로 예외 검사가 수행될 때 (Collections.sort(List) 메서드는 정렬하며, 객체 안에서 이미 비교할 수 없는 객체끼리 정렬한다면 내부에서 ClassCastException을 던짐)

 


⛓ Reference

이펙티브 자바 3판 - Item 49 : 매개변수가 유효한지 검사하라

Using Java Assertions - Baeldung

댓글