아이템69. 예외는 진짜 예외 상황에만 사용하라

oyeon·2022년 3월 27일
0

Effective Java

목록 보기
6/8

코드 69-1 예외를 완전히 잘못 사용한 예 - 따라 하지 말 것!

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

위 코드의 문제점

  1. 직관적이지 않다.
  2. 예외를 써서 루프를 종료하는 이상한 방식으로 구현

다음과 같이 표준 관융구대로 작성했다면 누구나 쉽게 이해했을 것이다.

코드 - 배열을 순회하는 표준 관용구

for(Mountain m : range)
  m.climb();

예외를 써서 루프를 종료한 이유? (코드 69-1)

  • 잘못된 추론을 근거로 성능을 높여보려 한 것
    • JVM은 배열에 접근할 때마다 경계를 넘지 않는지 검사
    • 일반적인 반복문도 배열 경계에 도달하면 종료(경계를 넘지 않는지 검사)

-> 따라서 이 검사를 반복문에도 명시하면 같은 일이 중복 되리라 판단하여 하나를 생략한 것이다.

잘못된 추론인 이유

  1. 예외는 예외 상황에 쓸 용도로 설계되었으므로 명확한 검사만큼 빠르게 만들어야 할 동기가 약하다.(즉, 예외처리는 최적화에 신경 쓰지 않았을 가능성이 크다.)
  2. 코드를 try-catch 블록 안에 넣으면 JVM이 적용할 수 있는 최적화가 제한된다.
  3. 배열을 순회하는 표준 관용구는 앞서 걱정한 중복 검사를 수행하지 않는다. JVM이 알아서 최적화해 없애준다.

추론과는 다르게 예외를 사용한 쪽이 표준 관용구보다 훨씬 느리다.

잘못된 예외 사용으로 인한 문제점

  1. 코드를 헷갈리게 한다.
  2. 오히려 성능을 떨어뜨릴 가능성이 크다.
  3. 코드 자체가 제대로 동작하지 않을 수 있다.
    • 예를 들어 코드 69-1 에서 내부와 관련없는 배열을 사용하다가 ArrayIndexOutOfBoundsException을 일으켰을 경우, 정상적인 반복문 종료 상황으로 오해하고 넘어갈 것이다.

교훈

  1. 예외는 오직 예외 상황에서만 써야 한다. 절대로 일상적인 제어 흐름용으로 쓰여선 안 된다.
    • 섵부른 최적화를 하지말아야 한다. 당장 성능이 더 좋을 수 있어도 자바가 계속 업그레이드 되며 성능 우위는 영원하지 않을 것이다.
    • 미묘한 버그로 인해 유지보수 문제는 계속 이어질 것이다.
  2. 잘 설계된 API라면 클라이언트가 정상적인 제어 흐름에서 예외를 사용할 일이 없게 해야 한다.
    • 특정 상태에서만 호출할 수 있는 '상태 의존적 메서드'를 제공하는 클래스는 '상태 검사 메서드'도 함께 제공해야 한다.
    • Ex. Iterator 인터페이스의 next : '상태 의존적 메서드', hasNext : '상태 검사 메서드'
    • 만약 hasNext가 제공되지 않는다면, 코드 69-1과 같이 예외를 통해 종료시켜야 할 것이다.
    • '상태 검사 메서드' 외의 선택지도 있다. '빈 옵셔널' 혹은 'null과 같은 특정 값'을 통해 반환하는 방법이다.

'상태 검사 메서드', '옵셔널', '특정 값'의 선택 기준

'옵셔널'이나 '특정 값' 선택

  1. '상태 검사 메서드'와 '상태 의존적 메서드' 호출 사이에 객체의 상태가 변할 수 있는 경우
    • 여러 스레드가 동시 접근, 외부 요인으로 상태 변할 수 있는 경우
  2. 성능이 중요한 상황에서 '상태 검사 메서드'가 '상태 의존적 메서드'의 작업 일부를 중복 수행

그 외 모든 경우에는 '상태 검사 메서드' 방식 사용

  • 가독성이 좋고, 오류를 발견하기 쉽다.
profile
Enjoy to study

0개의 댓글