Effective Java #38 인자의 유효성을 검사하라.
메서드와 생성자는 인자로 사용할 수 있는 값을 제한해야한다.(Null 또는 음수)
이런 제한들은 반드시 문서로 남겨야 할 뿐 아닐라 메서드 시작 부분에서 검사하여야 한다. 이런 검사를 통해 깔끔하고 신속하게 오류를 검출할 수 있으나 검사하지 못하면 몇가지 문제가 발생하게 된다.
1. 잘못된 예외로 인한 프로세스의 죽음
2. 실행은 되지만 잘못된 결과
3. 정상적인 반환값을 내기는 하지만 객체의 상태가 비정상적으로 바뀌는 경우
TIPS
public 메서드라면 인자 유효성이 위반되었을 경우에 발생하는 예외를 Javadoc의 @throws 태그를 사용해서 문서화 해야 한다.
인자 유효성에 관계된 조건뿐만 아니라 유효성 위반 시에 발생하는 예외까지 문서로 남겼다면 검사하는 코드를 만드는 것이 간단하다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class Test { public static void main(String[] args) { System.out.println(sum(0,-1)); } /** * 두 개의 파라미터 값을 더해 반환한다. * * @param p1 * @param p2 * @return p1 + p2 * @throws ArithmeticException(p1 < 0 || p2 < 0) */ public static int sum(int p1, int p2) { if(p1 < 0 || p2 < 0) throw new ArithmeticException("paramater < 0"); return p1 + p2; } } Exception in thread "main" java.lang.ArithmeticException: paramater < 0 at study.java.Test.sum(Test.java:19) at study.java.Test.main(Test.java:6) |
public 이 아닌 메서드는 일반적으로 인자 유효성을 검사할 때 확증문(assertion)을 이용한다.
확증문은 클라이언트가 패키지를 어떻게 이용하건 확증 조건(asserted condition)은 항상 참이 되어야 한다는 것이다.
통상적인 유효성 검사와는 달리 확증조건이 만족되지 않으면 Assertion Error을 낸다.
또한 통상의 유효성 검사와는 달리, 활성화 되지 않은 확증문은 실행되지 않으므로 비용이 0이다.
1 2 3 4 5 | public static void sort(long a[], int offset, int length) { assert a != null; assert offset >= 0 && offset <= a.length; assert length >= 0 && length <= a.length - offset; } |
생성자는 나중을 위해 보관될 인자의 유효성을 반드시 검사해야한다는 원칙의 특별한 경우에 해당한다. 클래스 불변식을 위반하는 객체가 만들어지는 것을 막으려면 생성자에 전달되는 인자의 유효성을 반드시 검사해야 한다.
메서드가 실제 계산을 수행하기 전에 그 인자를 반드시 검사해야 한다는 원칙에도 예외가 있다. 유효성 검사를 실행하는 오버헤드가 너무 크거나 비현실적이고, 계산 과정에서 유효성 검사가 자연스럽게 이루어 지는 경우이다.
예를들어 Collections.sort(List)처럼 객체 리스트를 정렬하는 메서드를 생각해보면 모두 서로 비교해야 정렬이 가능하다. 비교 가능하지 않으면 ClassCastException이 발생할 것이고 정렬 전에 모든 객체가 서로 비교 가능하닞 검사하는 것은 의미가 없어진다.
메서드나 생성자를 구현할 때는 받을 수 있는 인자에 제한이 있는지 따져봐야한다. 그리고 제한이 있다면 그사실을 문서에 남기고, 메서드 앞부분에서 검사하도록 해야 한다. 그래야 Debug하기 쉽고 개발이 편리해진다.