본문 바로가기
Study with group

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

by VENUSIM 2023. 7. 13.

메서드, 생성자에서 받은 매개변수가 특정 제약에 만족해야 한다면 오류는 발생한 곳에서 잡아야 하며 반드시 *문서화를 해야한다.

*문서화 (제약의 설명을 문서화 하고 예외의 경우 @throws 자바독 태그를 사용하여 문서화 할 수 있다.)

 

매개변수 검사를 제대로 하지 못하면 잘못된 결과, 모호한 예외, 해당 지점 이외에서 발생하는 문제를 초래할 수 있다.

다시 말해 *실패의 원자성을 지키기 위해 매개변수의 검사는 필요하다.

*실패의 원자성 (예외를 던지고 난 뒤에도 객체는 상태가 이전 상태와 동일하며 사용가능한 형태로 남아야한다.)

 

Objects Utility Class

NullPoniterExceptoin, IndexOutOfBoundsException 등의 예외에 대한 유효성 검사에 도움을 주는 Objects가 예로 나온다. Objects 는 자바 7버전 부터 지원하는 Utility Class이다. Null에 대한 유효성 검사를 지원하는 메서드의 경우 예외 메세지 커스텀이 가능하다.

반면 Index 유효성 검사를 지원하는 메서드의 경우 jdk.interal.util.Preconditions 에서 정의된 예외 메세지를 던진다. Index 유효성 검사 메서드에는 리스트와 배열 전용이고 닫힌 범위는 다루지 못한다는 몇가지 제약이 존재한다.

 

//NullPointerException
Objects.requireNonNull(null, "Objects.requireNonNull Test");

//IndexOutOfBoundsException
Objects.checkIndex(1,0);

 

 

Null 유효성 검사의 메서드 중 Supplier Interface를 인자로 받는 메서드도 제공한다.  Supplier Interface의 지연 연산의 이점을 활용한 예제이다.

 

// Null일 경우 Supplier Interface get() 호출을 통한 출력
Objects.requireNonNull(null, new LazySupplier());

// 대상 및 supplier 모두 Null check 지원 메서드
Objects.requireNonNullElseGet("", new LazySupplier());
System.out.println(Objects.requireNonNullElseGet(null, new LazySupplier()));
Objects.requireNonNullElseGet(null, (Supplier<String>) () -> null);

private static class LazySupplier implements Supplier {
   @Override
   public Object get() {
      Long start = System.currentTimeMillis();
      try {
          TimeUnit.SECONDS.sleep(3);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      return "This method took " + (System.currentTimeMillis() - start) / 1000 + " Seconds and It's Null";
   }
}

 

Object.requireNonNull()

 

Objects.requireNonNullElseGet()

 

Assert Keyword

공개 되지 않은 메서드에서 단언문(assert)를 통해 매개변수 유효성을 검증할 수 있다. 단언문에 참이 되어야 하는 조건을 부여하여 거짓이 반환 되었을 경우 실패하면 AssertionError를 던진다. 단순 옵션 없는 런타임에는 영향이 없지만 -ea (--enableassertions) 옵션을 통해 런타임 시점에 확인 가능하다.

 

public class AssertKeyword {
    public static void main(String[] args) {
        try {
            assertLengthCheckMethod(3 , 1,2);
        } catch (AssertionError e) {
            e.printStackTrace();
        }
    }

    private static void assertLengthCheckMethod(int length, int ... a) {
        assert a != null;
        assert a.length == length;
    }
}

 

 

계산 과정에서 필요한 유효성 검사가 이루어졌지만 문서화된 내용과 다른 예외를 던지는 경우가 발생 할 수 있는데, 이런 경우에는 *예외 번역 관용구를 사용하여 문서화 된 예외로 번역해 주어야한다.

*예외 번역 관용구 (상위 계층에서는 저수준 예외를 잡아 자신의 추상화 수준에 맞는 예외를 던져 주어야한다.)

 

결론

정리하자면 메서드는 최대한 범용적으로 설계해야하고, 메서드에 정의된 프로세스에 따라 정상적으로 동작 할 수 있다면 매개변수 제약은 적을 수록 좋다.

댓글