JAVA/Effective Java

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

꾸준함. 2024. 3. 7. 00:07

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

  • ex) 인덱스 값은 0 이상의 정수
  • ex) 객체 참조는 null이 아니어야 함

 

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

이는 `오류는 가능한 한 빨리 발생한 곳에서 잡아야 한다`는 일반 원칙의 한 사례입니다.

만약 오류를 즉시 잡지 않을 경우 해당 오류를 감지하기 어려워질뿐더러 감지하더라도 오류의 근원지를 찾기 어려워지며 최악의 상황에서는 실패 원자성을 어기는 결과를 낳을 수 있습니다.

  • 실패 원자성을 보장하기 위해서는 호출된 메서드가 실패하더라도 객체는 호출 전의 상태를 유지해야 함

 

 

 

코드 부연 설명

  • 앞서 언급한 일반 원칙을 따르기 위해
    • 생성자가 호출되는 시점에 매개변수를 확인하고 조건을 충족하지 못할 경우 예외를 던짐
    • 메서드가 실행되는 시점에 매개변수를 확인하고 조건을 충족하지 못 할 경우 예외를 던짐

 

  • 자바독 태그를 통해 문서화
    • 이런 간단한 방법으로 API 사용자가 제약을 지킬 가능성이 높아짐

 

매개변수 유효성 검사

  • public, protected API의 경우 공개된 API이기 때문에 유효성 검사 필요
  • List를 반환하는 등 메서드가 직접 사용하지는 않지만 나중에 쓰기 위해 저장하는 매개변수는 특히 신경 써서 검사해야 함
    • 만약 int 배열의 List 뷰를 반환하는 메서드에서 배열에 대한 Null 검사를 수행하지 않으면, 새로 생성한 List 인스턴스 역시 null
    • 이로 인해 클라이언트가 돌려받은 List에 대해 for-each문과 같은 명령을 수행하다가 런타임에 NullPointerException (NPE)가 발생할 수 있는 불상사가 발생 가능
    • 따라서 메서드에서는 배열이 null인지 여부를 체크하고, 필요한 경우에는 적절한 예외 처리를 통해 클라이언트를 안전하게 보호해야 함

 

 

 

  • private, package-private 메서드는 assert를 활용해 유효성 검사 가능
    • assert 문은 주로 디버깅을 위한 목적으로 사용되며, 주어진 조건이 참이 아닌 경우 AssertionError를 발생
    • 기본적으로 자바에서는 assert 문이 비활성되어 있기 때문에 릴리즈 빌드에서는 assert 문이 실행되지 않음
    • 따라서 assert로 인해 성능 저하가 발생하지 않음
    • 다만 java를 실행할 때 -ea 혹은 --enableassertions 플래그를 설정하면 런타임에 조건 미충족 시 AssertionError를 던지므로 영향을 끼침
    • 공개되지 않은 메서드라면 패키지 제작자가 메서드가 호출되는 상황을 통제할 수 있기 때문에 오직 유효한 값만이 메서드에 넘겨지리라는 것을 보증한다는 가정 하에 디버깅 목적인 assert문을 통해서만 유효성 검사

 

 

 

  • 생성자 매개변수 유효성 검사는 클래스 불변식을 어기는 객체가 만들어지지 않게 하기 위해 반드시 필요

 

매개변수 유효성 검사를 안 해도 되는 예외 케이스

 

다음은 매개변수 유효성 검사를 안해도 되는 예외 케이스들입니다.

  • 유효성 검사 비용이 높은 경우
  • 실용적이지 않은 경우
  • 암묵적 검사가 이미 이루어지는 경우

 

1. 유효성 검사 비용이 높은 경우

일부 경우에는 매개변수의 유효성을 검사하는 과정이 비용이 많이 들 수 있습니다.

예를 들어, 큰 데이터 집합이나 복잡한 연산이 필요한 경우에는 미리 유효성을 검사하는 것이 부담스러울 수 있습니다.

 

2. 실용적이지 않은 경우

매개변수의 유효성을 검사하는 것이 실용적이지 않을 때도 예외가 될 수 있습니다.

특히, 검사가 복잡하거나 실제로 발생할 가능성이 매우 낮은 경우에는 생략될 수 있습니다.

 

3. 암묵적 검사가 이미 이루어진 경우

일부 메서드는 내부적으로 암묵적으로 유효성을 검사하거나 처리할 수 있습니다.

예를 들어, Collections.sort(List) 메서드에서는 비교 과정에서 암묵적으로 유효성을 검사하기 때문에 별도 예외처리가 필요하지 않습니다.

하지만 암묵적 유효성 검사에 너무 의존할 경우 앞서 언급한 실패 원자성을 해칠 수 있으니 주의해야 합니다.

 

유용한 유효성 검사 API

 

책에서 소개한 유용한 유효성 검사 API는 다음과 같습니다.

  • Null 검사하는 자바 7+의 Objects.requireNonNull
  • 배열 범위와 크기가 유효한지 확인하는 자바 9+의 Objects.checkFromIndexSize
  • 리스트 범위가 유효한지 확인하는 자바 9+의 Objects.checkFromToIndex
  • 인덱스가 유효한지 확인하는 자바 9+의 Objects.checkIndex

 

정리

이번 아이템을 `매개변수에 제약을 두는 것이 좋다`고 해석해서는 안됩니다.

메서드가 건네받은 값으로 무언가 제대로 된 일을 할 수 있다면 매개변수 제약은 적을수록 좋지만 구현하려는 개념 자체가 특정한 제약을 내재한 케이스가 많습니다.

따라서 메서드나 생성자를 작성할 때면 그 매개변수들에 어떤 제약이 있을지 생각해야 하며 그 제약들을 문서화하고 메서드 코드 시작 부분에서 명시적으로 검사하는 것을 권장합니다.

 

참고

이펙티브 자바

반응형