이펙티브 자바, 쉽게 정리하기 - item 49. 매개변수가 유효한지 검사하라
오류 검사의 일반 원칙1: 오류를 즉시 잡아라
"오류는 가능한 한 빨리 발생한 곳에서 잡아야 한다."
- 오류를 발생한 즉시 잡지 못하면 해당 오류를 감지하기도 어렵고, 정확히 어디서 발생했는지 알기도 어렵다.
다양한 매개변수 검사 예시 (가장 간단한 원칙)
- 인덱스 값은 음수이면 안 된다.
- 객체 참조는
null
이 아니어야 한다.
매개변수 검사를 제대로 하지 못했을 때 벌어지는 일
- 메서드가 수행되다가 모호한 예외를 던지며 실패한다.
- 메서드가 잘 수행되지만 잘못된 결과를 반환한다.
- 메서드에서 사용한 다른 객체를 이상한 상태로 만들어 미래의 알 수 없는 시점에 문제가 발생한다.
아래 단계의 현상일수록 문제는 더욱 심각해진다.
예외의 문서화
- 자바독의
@throws
태그를 이용해서 가능하다. IllegalArgumentException
,IndexOutOFBoundsException
,NullPointerException
중 하나가 될 것이다.
예외 문서화 코드의 예시
/**
* 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} ≤ 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));
}
- 이 메서드의
@throws
태그는 0보다 작거나 같은 값의 나머지를 구하려 하면,ArithmeticException
이 발생한다고 친절하게 알려주고 있다.
All methods and constructors in this class throw {@code NullPointerException}
when passed a null object reference for any input parameter
- 클래스 전체 설명에는 위와 같이 모든 메서드와 생성자가
NullPointerException
을 던질 수 있다는 점을 내포하고 있다는 점도 볼만하다. 그래서 다른 메서드들에 따로@throws NullPointerException
을 적어주진 않고 있다.
자바7에서 추가된 null 검사 기능
public class Item49Test {
@Test
public void requireNonNullTest() {
Assertions.assertThrows(NullPointerException.class, () -> {
nullTest(null);
});
}
public void nullTest(Object obj) {
Objects.requireNonNull(obj, "널이 들어왔음");
}
}
Objects.requireNonNull()
덕에null
이 들어오면NullPointerException
예외를 던져준다.- 두번째 인수로 들어온
"널이 들어왔음"
문자열은 예외의 메시지로 들어간다.
자바의 assert
기능
@Test
@Test
public void requireNonNullTest2() {
Assertions.assertThrows(AssertionError.class, () -> {
nullTest2(null);
});
}
public void nullTest2(Object obj) {
assert obj != null;
}
- 위와 같이
assert
구문을 통해AssertionError
예외를 던지게 할 수도 있다.
오류 검사의 일반 원칙2: 나중에 쓰려고 저장하는 매개변수의 유효성을 검사하라
- 어떤 메서드에서 반환한 값을 나중에 쓰려고 보관해두었는데, 알고보니
null
이 들어있어 에러가 날 수 있다.- 이 경우에 어디서부터
null
이었는지 추적하는 과정이 꽤나 고통스러울 수 있으므로, 나중에 쓰려고 저장하는 매개변수의 경우 유효성을 먼저 검사해주는 것이 좋다.
- 이 경우에 어디서부터
일반 원칙2 의 예외들
- 유효성 검사 비용이 지나치게 높거나, 실용적이지 않을 때
- 계산 과정에서 암묵적으로 검사가 수행될 때
Collections.sort(List)
의 경우엔 정렬을 수행하기 때문에 계산 과정에서 상호 비교될 수 없는 타입이 들어있다면ClassCastException
을 던질 것이다.- 암묵적 유효성 검사에 너무 의존하면 실패 원자성을 해치니 주의하자.
- 실패 원자성이란, 유효성 검사에 실패한 이후에 객체의 상태가 영구적으로 바뀌어 다음 실행 결과가 다른 경우를 의미한다.
- 메서드가 여러번 실패해도 매번 같은 결과를 반환해야 실패 원자성이 지켜지는 것이다.
- 실패 원자성이란, 유효성 검사에 실패한 이후에 객체의 상태가 영구적으로 바뀌어 다음 실행 결과가 다른 경우를 의미한다.
- 계산 중 던져지는 예외와 API에서 던지기로 한 예외가 다를 수 있다는 뜻이다.
- 이 경우 저수준의 예외를 잡아 고수준의 사용자정의 예외로 바꿔주는 예외 번역이 필요할 수 있다.
핵심 정리
- 매개변수에 어떠한 제약이 있을지 생각하며 메서드를 작성하자.
- 에러가 날만한 부분을 미리 걸러내서 나중에 복잡한 에러를 만들지 말자.
단, 무작정 매개변수에 많은 제약을 걸진 말자. 메서드는 최대한 범용적으로 작성되는 것이 맞다.
반응형
'Java > 이펙티브 자바' 카테고리의 다른 글
이펙티브 자바, 쉽게 정리하기 - item 51. 메서드 시그니처를 신중히 설계하라 (0) | 2023.06.07 |
---|---|
이펙티브 자바, 쉽게 정리하기 - item 50. 적시에 방어적 복사본을 만들라 (0) | 2023.06.07 |
이펙티브 자바, 쉽게 정리하기 - item 48. 스트림 병렬화는 주의해서 적용하라 (0) | 2023.05.31 |
이펙티브 자바, 쉽게 정리하기 - item 47. 반환 타입으로는 스트림보다 컬렉션이 낫다 (0) | 2023.03.31 |
이펙티브 자바, 쉽게 정리하기 - item 46. 스트림에서는 부작용 없는 함수를 사용하라 (0) | 2023.03.30 |