#57 예외 상황에서만 예외를 사용하자
// 이렇게 예외 이용 X
try {
int i = 0;
while(true) {
range[i++].climb();
}
} catch (ArrayIndexOutOfBoundsException e) {
}
while문을 이용해서 range 배열에 있는 모든 객체의 climb() 메서드를 수행하는 코드처럼? 보입니다.
만약 인덱스 값(i)이 range 배열의 크기(size) 보다 커지면, try-catch문으로 ArrayIndexOutOfBoundException 예외 발생을 검출하고, 무시하면서 이 무한 while문은 종료됩니다.
for (Mountain m : range) {
m.climb();
}
이를 for문을 이용하면 좀 더 쉽게 구현할 수 있습니다.
둘 다 같은 동작과 결과를 얻을 수 있습니다.
그렇다면 왜 일반적인 코드로 처리 가능한 상황에서 예외를 쓰지 말아야 하는지에 대해 알아보겠습니다.
#01 성능 저하
기본적으로 예외는 예외 상황을 대비해 설계되었기 때문에, JVM에서 굳이 예외 처리 속도 향상이 중요하지는 않습니다.
따라서 예외 처리 코드 속도가 일반 코드보다 더 느릴 수 있습니다.
#02 가독성 저하
일반 코드를 예외 처리 코드로 만들면, 코드의 본래 목적을 혼란스럽게 하기 때문에 가독성이 떨어질 수 있습니다.
#03 부작용 발생
try-catch문을 통해 예외가 발생해도 무시될 수 있기 때문에, 개발자도 모르는 부작용이 발생할 수 도 있습니다.
이는 디버깅이 무척 어렵게 만들 수 있습니다.
기본적으로 잘 설계된 API는 클라이언트가 예외를 사용해서 정상적인 흐름을 제어하도록 하지 않습니다.
while문을 이용해서 Collection 내부에 저장된 Foo 객체들을 하나씩 꺼내는 코드입니다.
Iterator<Foo>에서 더 이상 꺼낼 객체가 없으면 NoSuchElementException 예외가 발생하고, 이를 검출-무시하고 있습니다.
try {
Iterator<Foo> i = collection.iterator();
while(ture) {
Foo foo = i.next();
}
} catch (NoSuchElementException e) {
}
Iterator 인터페이스는 상태-종속(state-dependant) 메서드와 상태-검사(state-testing) 메서드를 가지고 있습니다.
next : 상태-종속 메서드
hasNext : 상태-종속 메서드를 호출하는 것이 적합한지 나타내는 상태-검사 메서드
for(Iterator<Foo> i = collection.iterator(); i.hasNext();) {
Foo foo = i.next
}
상태-검사 메서드를 통해 예외를 이용하지 않고 정상적인 코드 작성이 가능합니다.
이처럼 예외는 예외적인 상황에서만 이용해야 합니다.
그리고 클라이언트를 위한 API를 작성할 때는 예외를 통해 정상적인 흐름을 제어하도록 설계하면 안 됩니다.