Item 69. 예외는 진짜 예외 상황에만 사용하라

Posted by yunki kim on April 17, 2022

  코드 부터 보자.

1
2
3
4
5
6
7
8
9
try {
    int i = 0;
    while (true) {
        range[i++].climb();
    }
catch (ArrayIndexOutOfBoundsException e) {
 
}
 
cs

  이 코드는 배열의 끝에 도달하면 에러를 던져 코드를 종료한다. for-each loop 를 사용하지 않고 이처럼 작성한 이유는 다음과 같다. JVM은 배열에 접근할 때마다 경계를 넘었는지를 검사한다. 반복문 또한 배결 경계에 도달하면 종료한다. 따라서 반복문을 사용하면 경계 검사가 중복되므로 하나를 생량한 것이다. 하지만 이는 다음과 같은 이유로 잘못된 추론이다.

  1. 예외는 예외 상황에서만 사용할 목적으로 설계되었으므로 JVM 구현자 입장에서는 명확한 검사만큼 최적화를 하지 않았을 가능성이

      크다.

  2. 코드를 try-catch 블록 안에 넣으면 JVM이 적용할 수 있는 최적화가 제한된다.

  3. 배열을 순회하는 표준 관용구는 앞서 말한 중복 검사를 수행하지 않는다. JVM이 알아서 최적화해 없애준다.

  또 한, 이런 예외 처리는 디버깅을 어렵게 한다. 통상적인 반복문 안에 버그가 숨겨져 있어서 ArrayIndexOutOfBoundsException을 일으켰다 해보자. 그러면 이 버그는 스택 추적 정보를 남기고 해당 스레드를 즉작 종료시킨다. 반면 위 코드 같은 예외를 사용한 반복문은 버그 때문에 발생한 예외를 정상적인 반복문 종료 상황으로 오해하고 넘어간다.

  따라서 예외는 오직 예외 상황에서만 사용하라. 절대로 일상적인 제어 흐름용으로 쓰여선 안 된다. 성능 개선을 목적으로 과하게 복잡한 기법을 자제하라. 자바 플랫폼은 꾸준히 개선되고 있다. 따라서 이로 인한 성능 우위가 오래가지 않을 수 있다.

  이 원칙은 잘 설계된 API에도 적용된다. 잘 설계된 API는 클라이언트가 정상적인 제어 흐름에서 예외를 사용할 일이 없게 해야한다. 그 대신, '상태 의존적' 메서드를 제공하는 클래스가 '상태 검사' 메서드를 제공해 줘야 한다. Iterator 인터페이스를 예로 들자면, '상태 의존적' 메서드는 next이고 '상태 검사' 메서드는 hasNext이다.

  상태 검사 메서드를 대신 옵셔널과 특정 값(null 등)을 사용해도 된다. 이들 중 하나를 선택하는 지침은 다음과 같다.

  1. 외부 동기화 없이 여러 스레드가 동시에 접근할 수 있거나  외부 요인으로 상태가 변할 수 있다면 옵셔널이나 특정 값을 사용한다. 상태 검사 메서드와 상태 의존적 메서드 호출 사이에 객체의 상태가 변할 수 있기 때문이다.

  2. 성능이 중요한 상황에서 상태 검사 메서드가 상태 의존적 메서드의 작업 일부를 중복 수행한다면 옵셔널이나 특정 값을 선택한다.

  3. 다름 모든 경우엔 상태 검사 메서드 방식이 좋다. 가독성이 더 좋고 잘못된 사용을 발견하기 쉽다. 사애 검사 메서드 호출을 잊었다면 상태 의존적 메서드가 예외를 던져 버그를 확실히 드러낼 것이다. 반면 특정 값은 검사하지 않고 지나쳐도 발션하기 어렵다(옵셔널에는 해당되지 않는다).

 

출처 - 이펙티브 자바