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

신명철·2022년 3월 15일
0

Effective Java

목록 보기
47/80

들어가며

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

매개변수 검사를 제대로 수행하지 못하면 다음과 같은 문제가 생길 수 있다.

  • 메서드가 수행되는 중간에 모호한 예외를 던지며 실패할 수 있다.
  • 더 나쁜 상황은 메서드가 잘 수행되지만 잘못된 결과를 반환하는 상황이다.
  • 한층 더 나쁜 상황은 어떤 객체를 이상한 상태로 만들어 놓아서 미래의 알 수 없는 시점에 이 메서드와는 관련 없는 오류를 낼 때다.

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

매개변수의 제약을 문서화한다면 그 제약을 어겼을 때 발생하는 예외도 함께 기술해야 한다. 다음 예시를 보자

	/*
	 * (현재 값 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);
		
        ... // 계산 수행
    }
  • 위 메서드는 m 이 null 이면 NPE 를 반환하지만 NPE 를 반환한다는 내용을 기술하지 않고 있다. 그 이유는 해당 내용을 개별 메서드가 아닌 클래스 수준에서 이미 기술했기 때문이다.
  • @Nullable 을 사용해서 기술할 수도 있지만 이는 표준적인 방법은 아니다.

자바 7에 추가된 java.utils.Objects.requireNonNull메서드는 유연하고 사용하기도 편하다. null 검사를 수동으로 진행하지 않아도 된다. 반환하는 값을 사용하는 동시에 null 검사를 수행하고 예외 메시지도 지정할 수 있다. 다음 코드를 보자.

this.strategy = Objects.requireNonNull(strategy, "전략");

public 이 아닌 메서드

외부로 공개되지 않는 메서드라면 사용자가 메서드에 오직 유효한 값만 넘겨지리라는 것을 보증할 수 있다. 이런 경우assert를 사용해서 매개변수 유효성을 검증할 수 있다.

private static void sort(long a[], int offset, int lenght){
	assert a != null;
    assert offset >= 0 && offset <= a.length;
    assert length >= 0 && length <= a.length - offset;
    
    ... // 계산 수행
}
  • 단언문들은 자신이 선언한 조건이 무조건 참이라고 선언한다.

단언문은 몇 가지 면에서 일반적인 유효성 검사와 다르다.

  • 첫 번째, 실패하면 AssertionError를 던진다.
  • 두 번째, 런타임에 아무런 효과도, 아무런 성능 저하도 없다.

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

메서드가 사용하지 않지만 나중에 쓰기 위해 저장하는 매개변수는 특히 더 신경써야한다.

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

위의 코드는 int 배열을 List로 변경해주는 정적 팩터리 메서드이다. 만약 requireNonNull을 이용해 배열의 유효성을 검사하지 않았다면 사용자는 List를 받아서 사용하려고 하는 시점에서야 NPE을 받게 될 것이다. 이런 경우 오류가 발생한 지점을 추적하기가 어려워진다.

메서드 몸체 실행 전 유효성 검사의 예외

매개변수의 유효성을 메서드 몸체 실행 전에 검사해야 한다는 규칙에도 예외는 있다. 메서드 수행 중 암묵적으로 매개변수의 유효성이 검사되는 경우이다.

예를 들어서 Collections.sort(List) 처럼 객체 리스트를 정렬하는 메서드는 리스트 안에 서로 비교될 수 없는 타입의 객체가 들어있다면 ClassCastException을 던진다. 이런 경우는 굳이 유효성을 검사하는 것은 실용적이지 않다.

하지만 때로는 계산 과정 중에 이루어지는 유효성 검사가 잘못된 예외를 던지기도 한다. 계산 중 잘못된 매개변수 값을 사용해서 발생한 예외와 API 문서에서 던지기로 한 예외가 서로 다를 수가 있다는 뜻이다. 이런 경우엔 아이템 73 에서 설명할 예외 번역 관용구를 사용해 API 문서에 기재한 대로 예외를 번역해줘야 한다.

메서드는 최대한 범용적으로 설계해야 한다. 메서드가 건네 받은 값으로 무언가 제대로 된 일을 할 수 있다면 매개변수 제약은 적을수록 좋다.

profile
내 머릿속 지우개

0개의 댓글