이 글은 책 이펙티브 자바 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)
- 실패하면 AssertionError를 던진다.
- 런타임에 실행되지 않는다.(성능 저하 없음, java 실행 시 옵션으로 -ea (—enableassertions) 플래그 설정하면 작동)
보통 개발 중에 테스트 목적으로 사용된다.
+a) 꼭 메서드 로직(몸체) 실행 전 매개변수 유효성 검사를 해야 할까?
- 유효성 검사 비용이 높거나 실용적이지 않을 때는 다른 방법을 고려하자. (예외 던지는 대신 다른 방법을 두어야겠다.)
- 계산 과정에서 암묵적으로 예외 검사가 수행될 때 (Collections.sort(List) 메서드는 정렬하며, 객체 안에서 이미 비교할 수 없는 객체끼리 정렬한다면 내부에서 ClassCastException을 던짐)
⛓ Reference
이펙티브 자바 3판 - Item 49 : 매개변수가 유효한지 검사하라
'☕️ JAVA > 🦋 Effective Java' 카테고리의 다른 글
생성자에 매개변수가 많다면 빌더 패턴을 써볼까?! (HTTPClient와 lombok @Builder) 🧰 (0) | 2022.02.11 |
---|---|
표준 예외를 사용하자 (0) | 2022.02.06 |
자바에서 객체 복사는 clone()이 아닌 복사 생성자, 팩토리를 사용하자 (0) | 2022.02.05 |
자바의 동시성 프로그래밍, 가변 데이터를 동기화하는 3가지 방법(+a. 자바 기본 타입의 원자성에 대하여) (0) | 2022.01.24 |
자바 enum에서 ordinal 메서드 사용하지마...(세요) 🚫 (1) | 2022.01.21 |
댓글