Skip to content

Latest commit

 

History

History
99 lines (82 loc) · 5.59 KB

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

File metadata and controls

99 lines (82 loc) · 5.59 KB

아이템 49: 매개변수가 유효한지 검사하라

메서드는 검증이 필요하고 예외는 빠를수록 좋다.

  • 메서드와 생성자 대부분은 입력 매개변수의 값이 특정 조건을 만족하기를 바란다.
    • 인덱스 값은 음수일 수 없다
    • 객체 참조는 null이면 안된다.
  • 이러한 제약은 메서드 몸체가 시작되기 전에 검사되어야 한다.
  • 이는 오류는 가능한 빨리 발생한 곳에서 잡아야 한다는 일반 원칙의 사례이기도 함
  • 오류를 발생한 즉시 잡지 못하면 오류는 감지하기 어려워지고 발생 지점을 추적하기 또한 어려워진다.
  • 반면 메서드 몸체가 실행되기 전에 매개변수를 확인한다면 잘못된 값이 넘어왔을 때 즉각적이고 깔끔한 방식으로 예외를 던질 수 있다.

매개변수 검사를 제대로 하지 못하면 생길 수 있는 문제점

  1. 메서드가 수행되는 중간에 모호한 예외를 던지며 실패할 수 있다.
  2. 예외를 던지지 않고 잘못된 결과를 반환할 수 있다 (1번 보다 심각)
  3. 메서드는 문제 없이 수행되었지만 어떤 객체를 이상한 상태로 만들어놓아서 미래의 알 수 없는 시점에 관련 없는 오류 발생 (2번 보다 심각)
  • 이를 통해 매개변수 검사에 실패하면 실패 원자성을 어기는 결과를 낳을 수 있다는 것을 알 수 있음
  • 매개변수 검사를 해야하는 이유를 알아보았다.
  • 이어지는 요약에서는 매개변수 값을 검사하는 여러가지 방법이 등장한다.

메서드는 던질 수 있는 예외를 문서화해야한다.

  • public, protected 메서드는 매개변수 값이 잘못됐을 때 던지는 예외를 문서화해야 한다.
  • @throws 자바독 태그를 사용하여 처리할 수 있다.
  • 그리고 매개변수의 제약을 문서화한다면 그 제약을 어겼을 때 발생하는 예외도 함께 기술해야 한다.
  • 이런 간단한 처리는 API 사용자가 제약을 지킬 가능성을 크게 높일 수 있다.

매개변수 검증 예시

 
/**
 * 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이면 NPE 발생
  • 그런데 m이 null일 때 NPE를 발생시킨다라는 말은 메서드 설명 어디에도 없다.
  • 그 이유는 이 설명을 BigInteger 클래스 수준에서 기술했기 때문
  • 클래스 수준의 주석은 그 클래스의 모든 public 메서드에 적용됨으로 일일히 표기하지 말도록 하자.

제공되고 있는 유용한 검사 기능 소개

  • 검사를 다음 처럼 수동으로 할 수도 있다.
if (param == null) {
	throw new NullPointerException();
}
  • 다만 다음의 기능들 처럼 이미 제공되고 있는 기능도 있으니 참고 해보자
    • java.util.Objects.requireNonNull: null 검증
    • java.util.Objects.checkFromIndexSize: 범위 검사 - 1
    • java.util.Objects.checkFromToIndex: 범위 검사 - 2
    • java.util.Objects.checkIndex: 범위 검사 - 3

private 메서드는 우리가 상황을 통제할 수 있다.

  • 공개되지 않은 메서드라면 패키지 제작자인 우리가 메서드가 호출되는 상황을 통제할 수 있다.
  • 따라서 오직 유효한 값만이 메서드에 넘겨지리라는 것을 우리가 보증할 수 있고, 그렇게 해야 한다.
  • 이러한 경우 단언문(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;
}
  • 위는 단언문을 사용한 private 메서드 예시이다.
  • 여기서 단언문들은 자신이 단언한 조건이 무조건 참이라고 선언한다.
  • 단언문들은 실패하면 AssertionsError를 던지고 런타임에 아무런 성능저하도 없다는 것이 일반적인 유효성 검증과의 차이이다.

메서드가 직접 사용하지는 않으나 나중에 쓰기 위해 저장하는 매개변수

  • 메서드가 직접 사용하지는 않으나 나중에 쓰기 위해 저장하는 매개변수는 특히 더 신경 써서 검사해야 한다.
  • 생성자는 "나중에 쓰려고 저장하는 매개변수의 유효성을 검사하라"는 원칙의 특수한 사례다.
  • 생성자 매개변수의 유효성 검사는 클래스 불변식을 어기는 객체가 만들어지지 않게 하는 데 꼭 필요하다.

유효성 검사는 항상 진행되어야 하나?

  • 유효성 검사 비용이 지나치게 높거나 실용적이지 않을 때, 혹은 계산 과정에서 암묵적으로 검사가 수행되는 경우 유효성 검사는 생략될 수도 있다.

  • 이번 아이템 요약을 매개변수에 제약을 두는 게 좋다라고 해석해서는 안된다.

  • 사실은 그 반대다.

  • 메서드는 최대한 범용적으로 설계되어야 하며 제대로 된 일을 할 수 있다면 매개변수 제약은 적을수록 좋다.

  • 하지만 구현하려는 개념 자체가 특정한 제약을 내재한 경우도 드물지 않다.