Home [Effective Java] Item69. 예외는 진짜 예외 상황에만 사용하라!
Post
Cancel

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

Item69. 예외는 진짜 예외 상황에만 사용하라!

예외를 잘못 사용한 경우

1
2
3
4
5
6
try {
    int i = 0;
    while(true)
        range[i++].climb();
} catch (ArrayIndexOutOfBoundsException e) {
}
  • 위 예제를 보고 무엇을 나타내는 코드인지 파악할 수 없다.
  • 전혀 직관적이지 않다는 사실 하나만으로도 코드를 이렇게 작성하면 안 되는 이유는 충분하다.
1
2
3
for(Mountain m : range) {
    m.climb();
}
  • 같은 동작을 하는 코드인데, 이처럼 표준적인 관용구대로 작성했다면 곧바로 이해 가능하다.

왜 예외를 써서 루프를 종료했을까?

  • 잘못된 추론을 근거로 성능 향상을 노렸기 때문이다.
  • JVM은 배열에 접근할 때마다 경계를 넘는지 체크한다. 이 검사를 반복문에도 명시하면 같은 일이 중복되니 이를 생략한 것이다.
  • 하지만 이러한 추론은 세 가지 면에서 잘못되었다.

잘못 추론된 이유

  1. 예외는 예외 상황에서 쓸 용도로 설계되었으므로 JVM 구현자 입장에서는 명확한 검사만큼 빠르게 만들어야 할 동기가 약하다.(실제로 최적화에 신경 별로 쓰지 않았을 가능성 큼.)
  2. 코드를 try-catch 블록 안에 넣었는데, 이렇게 하면 JVM이 적용할 수 있는 최적화가 제한됨.
  3. 배열을 순회하는 표준 관용구는 앞서 걱정한 중복 검사를 수행하지 않는다는 것을 간과함. JVM이 일아서 최적화하고 없애주기 때문.

잘못된 추론의 결과

  • 예외를 사용한 쪽이 2배 정도 느림.
  • 제대로 동작하지 않을 가능성 있음.
    • 반복문 안에 버그가 숨어있다면, 흐름 제어에 쓰인 예외가 디버깅을 훨씬 어렵게 한다.

교훈

  • 예외는 (그 이름이 말해주듯) 오직 예외 상황에서만 써야 한다. 절대로 일상적인 제어 흐름용으로 쓰여선 안 된다.
  • 표준적이고 쉽게 이해되는 관용구를 사용하자.
  • 성능 개선을 목적으로 과하게 머리를 쓴 기법은 자제하자.

API 설계와 예외

  • 잘 설계된 API라면 클라이언트가 정상적인 제어 흐름에서 예외를 사용할 일이 없게 해야 한다.
  • 특정 상태에서만 호출할 수 있는 ‘상태 의존적’ 메서드를 제공하는 클래스는 ‘상태 검사’ 메서드도 함께 제공하자.
    • Iterator의 경우, hasNext 메서드를 제공함으로써 클라이언트가 직접 배열의 상태를 검사하는 예외를 사용하지 않아도 되도록 했다.

상태 검사 메서드의 대안

  • 올바르지 않은 상태일 때 빈 옵셔널 혹은 null 같은 특수한 값을 반환하는 방법이다.
  1. 외부 동기화 없이 여러 스레드가 동시 접근 가능하거나 외부 요인으로 상태가 변할 수 있다면 옵셔널이나 특정 값 사용.
  2. 성능이 중요한 상황에서 상태 검사 메서드가 상태 의존적 메서드의 작업 일부를 중복 수행한다면 옵셔널이나 특정 값 사용.
  3. 다른 모든 경우, 상태 검사 메서드 방식이 더 낫다.

핵심 정리

  • 예외는 예외 상황에서 쓸 의도로 설계되었다.
  • 정상적인 제어 흐름에서 사용해서는 안 되며, 이를 프로그래머에게 강요하는 API를 만들어서도 안 된다.
This post is licensed under younghwani by the author.

[Effective Java] Item68. 일반적으로 통용되는 명명 규칙을 따르라!

[Effective Java] Item70. 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라!

Comments powered by Disqus.