메서드와 생성자 대부분은 매개변수의 값을 정하는 데 제약을 주는데, 이는 가능한 한 오류를 빠르게 잡아야 하기 때문이다.
따라서 메서드 몸체가 실행되기 전에 매개변수를 확인하여 즉각적으로 예외를 던질 수 있게 해야한다.
매개변수 검사를 제때 하지 못하면 몇 가지 문제가 발생할 수 있다.
1. 메서드가 수행되는 중간에 모호한 예외를 던지며 실패할 수 있다.
2. 메서드가 잘 수행되지만 잘못된 결과를 반환할 수 있다.
3. 메서드는 문제없이 수행됐지만, 어떤 객체를 '이상한' 객체로 만들어 미래에 알 수 없는 시점에 해당 메서드와 관련 없는 오류를 낼 수 있다.
즉, 매개변수 검사에 실패하면 실패 원자성(failure atomicity)를 어기는 결과를 낳을 수 있다.
public, protected 제어자는 외부 접근도가 높은 제어자이다.
따라서 public, protected 메서드에는 @throws 자바독 태그를 통해 매개변수 값이 잘못되었을 때 던지는 예외를 문서화해야한다.
(보통은 IllegalArgumentException, IndexOutOfBoundsException, NullPointerException 중 하나일 것..)
또한 문서화를 했으면 그 제약을 어겼을 때 발생하는 예외도 함께 기술해야한다.
이를 통해 API 사용자가 제약을 지킬 가능성을 높일 수 있다.
이를 잘 활용한 BigInteger class의 mod method를 들고와봤어용~
"All methods and constructors in this class throw NullPointerException
when passed a null object reference for any input parameter. "
//BigInteger must support values in the range -2Integer.
//MAX_VALUE (exclusive) to +2Integer.MAX_VALUE (exclusive) and
//may support values outside of that range.
//An ArithmeticException is thrown when a BigInteger constructor or method
//would generate a value outside of the supported range.
//The range of probable prime values is limited and
//may be less than the full supported positive range of BigInteger.
//The range must be at least 1 to 2500000000.
/**
* 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));
}
이 메서드의 매개변수 m에 0과 같거나 작은 수가 온다면 이 메서드는 NullPointerException을 던진다.
위 메서드에서는 해당 설명을 개별 메서드가 아닌 BigInteger class 수준에서 정의했는데,
클래스 수준 주석은 해당 클래스의 모든 public method에 적용되므로 훨씬 깔끔한 방법이다.
Java 7에 추가된 java.util.Objects.requireNonNull 메서드는 유연하고 사용하기도 편하다.
따라서 더 이상 Null 검사를 수동으로 하지 않아도 된다.
원하는 예외 메서드도 지정할 수 있고, 입력을 그대로 반환하므로 값을 사용하는 동시에 null 검사를 수행할 수 있다.
this.strategy = Object.requireNotNull(strategy, "전략");
Java 9에서는 Objects범위 검사 기능도 더해졌다.
checkFromIndexSize, checkFormToIndex, checkIndex 메서드가 그 예이다. (null 검사 메서드만큼 유연하진 않다.)
원하는 예외 메서드를 지정할수는 없고, 리스트와 배열 전용으로 설계되었고 닫힌 범위는 다루지 못한다.
해당 상황들이 아닌 상황에서는 아주 유용하고 편리하다!
public이 아닌 메서드라면 assert를 사용해 매개변수 유효성을 검증할 수 있다.
assert들은 자신이 단언한 조건이 무조건 참이라고 선언한다. 이 메서드가 포함된 패키지를 클라이언트가 어떤 식으로 다루는지는 상관 없다.
[ 단언문의 유효성 검사가 일반적인 유효성 검사와 다른 점 ]
1. 실패하면 AssertionError을 던진다.
2. 런타임에서 아무런 효과,성능 저하가 없다.
메서드가 직접 사용하지는 않지만 나중에 쓰기 위해서 저장하는 매개변수는 특히 더 신경써서 검사해야 한다.
또한, 생성자 매개변수의 유효성 검사는 클래스 불변식을 어기는 객체가 만들어지지 않게 하기 위해서 꼭 필요한 검사이다.
메서드 몸체 실행 전에 매개변수 유효성을 검사해야 한다는 규칙의 예외
1. 유효성 검사 비용이 지나치게 높거나 실용적이지 않은 경우
2. 계산 과정에서 암묵적으로 검사가 수행되는 경우 ( 너무 여기에 의존했다가는 실패 원자성을 해칠 수 있으니 유의해야한다.)
핵심 정리
메서드는 최대한 범용적으로 설계해야 한다.
메서드나 생성자를 작성할 때면 그 매개변수들에 어떤 제약이 있을지 검사해야 한다.
제약들을 문서화하고, 메서드 코드 시작 부분부터 명시적으로 검사해야 한다.
'JAVA > Effective Java' 카테고리의 다른 글
item 51. 메서드 시그니처를 신중히 설계하라 (0) | 2022.07.10 |
---|---|
item 50. 적시에 방어적 복사본을 만들라 (0) | 2022.07.10 |
item 48. 스트림 병렬화는 주의해서 적용해라 (0) | 2022.07.02 |
item 47. 반환 타입으로는 스트림보다 컬렉션이 낫다 (0) | 2022.07.02 |
item 46. 스트림에서는 부작용 없는 함수를 사용하라 (0) | 2022.07.02 |
댓글