[Effective Java] 예외는 진짜 예외 상황에서만 사용하라

들어가며

예외는 예외적인 상황에만 써야한다. 다음 코드를 보자.

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

여기서 예외를 써서 반복문을 종료한 이유는 잘못된 추론을 근거로 성능을 높여보려 했기 때문이다. JVM은 배열에 접근할 때마다 경계를 넘지 않는지 검사하는데 일반적인 반복문도 배열 경계에 도달하면 종료한다. 따라서 이 검사를 반복문에도 명시하면 같은 일이 중복되므로 하나를 생략한 것이다. 하지만 이는 잘못되었다.

  1. 예외는 예외상황에 쓸 용도로 설계되었으므로 JVM 구현자 입장에서는 명확한 검사만큼 빠르게 만들어야할 동기가 약하다.
  2. 코드를 try-catch 블록안에 넣으면 JVM이 수행해주는 최적화에 제한이 걸린다.
  3. 배열을 순회하는 표준 관용구는 앞서 언급한 중복 검사를 수행하지 않는다. 즉 JVM이 알아서 최적화 해준다.

여기서 알 수 있듯이 예외는 예외적인 상황에만 써야한다. 절대로 일상적인 제어 흐름용으로 쓰여선 안된다. 이 원칙은 API 설계에도 적용된다. 잘 설계된 API라면 클라이언트가 정상적인 제어 흐름에서 예외를 사용할 일이 없게 해야 한다. 특정 상태에서만 호출할 수 있는 상태 의존적 메소드를 제공하는 클래스는 상태 검사 메소드도 함께 제공해야한다. Iterator의 next와 hasNext가 그 예이다.

상태검사 메소드 대신 선택할 수 있는 방안도 몇개 있다. Optional 메소드라던가 return null이 그 예이다. 다음은 이를 선택하는 지침이다.

  1. 외부 동기화 없이 여러 스레드가 동시에 접근할 수 있거나 외부 요인으로 상태가 변할 수 있다면 Optional이나 특정 값을 사용한다.
  2. 성능이 중요한 상황에서 상태 검사 메소드가 상태 의존적 메소드의 작업 일부를 중복 수행한다면 Optional이나 특정 값을 선택한다.
  3. 다른 모든 경우엔 상태 검사 메소드 방식이 조금 더 낫다.
Author: Song Hayoung
Link: https://songhayoung.github.io/2020/08/21/Languages/Effective%20JAVA/item69/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.