예외의 잘못된 사용
다음은 예외를 잘못 사용한 예시이다.
try {
int i = 0;
while(true)
range[i++].climb();
}catch (ArrayIndexOutOfBoundException e) {
}
이 코드의 문제는 다음과 같다.
- 전혀 객관적이지 않다. 다음과 같이 작성하면 모든 사람이 직관적으로 이해할 수 있다.
for (Mountain m : range)
m.climb();
- 제대로 동작하지 않을 수 있다.
- 반복문안에 잘못된 코드가 숨어져 있다면 흐름 제어에 쓰인 예외가 이 버그를 숨겨 디버깅을 더 어렵게 만든다.
예외는 필요할 때만 사용하자
예외는 오직 예외 상황에서만 사용해야 한다. 절대로 일상적인 제어 흐름용으로 쓰여서는 안 된다. 이 원칙은 API 설계에도 적용된다. 잘 설계된 API라면 클라이언트가 정상적인 제어 흐름에서 예외를 사용할 일이 없게 해야 한다. 예를 들어 특정 상태에서만 호출할 수 있는 '상태 의존적' 메서드를 제공하는 클래스는 '상태 검사' 메서드도 함께 제공해야 한다.
- e.g ) Iterator 인터페이스의 next와 hasNext가 각각 상태 의존적 메서드와 상태 검사 메서드에 해당한다.
- 나눗셈을 할 때 0으로 나누는 경우 예외가 아닌 N/A를 리턴하도록 만들 수 있다.
public static String divide(int a, int b) {
if (b == 0) {
System.out.println("b는 0이 될 수 없습니다.");
return "N/A";
}
return String.valueOf(a / b);
}
Iterator가 상태 검사 메서드를 제공함으로써 전통적인 for문의 활용도 가능하다.
for(Iterable<Foo> i = collection.iterator(); i.hasNext(); ) {
Foo foo = i.next();
}
만약 상태 검사 메서드를 제공하지 않았다면 그 일을 클라이언트가 대신해야 한다.
try {
Iterator<Foo> i = collection.iterator();
while(true) {
Foo foo = i.next();
...
} catch (NoSuchElementException e) {
}
}
상태 검사 메서드 대신 옵셔널을 사용하거나 null 같은 특수한 값을 반환할 수 있다. 상황에 따라서 적절한 방법을 선택하자.
- 외부 동기화 없이 여러 스레드가 동시에 접근할 수 있거나 외부 요인으로 상태가 변할 수 있다면 옵셔널이나 특정 값을 사용한다.
-> 상태 검사 메서드와 상태 의존적 메서드 호출 사이에 객체의 상태가 변할 수 있다. - 성능이 중요한 상황에서 상태 검사 메서드가 상태 의존적 메서드의 작업 일부를 중복 수행한다면 옵셔널이나 특정 값을 선택한다.
- 다른 모든 경우엔 상태 검사 메서드 방식이 조금 더 낫다.
-> 가독성이 더 좋고, 상태 검사 메서드 호출을 잊어버렸다면 상태 의존적 메서드가 예외를 던져 버그를 확실하게 잡아준다.