예외

예외를 제대로 활용한다면 프로그램의 가독성, 신뢰성, 유지보수성이 높아지지만, 잘못 사용하면 반대의 효과만 나타난다. 예외를 효과적으로 활용해보자.

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

핵심 정리

예외는 예외 상황에서 쓸 의도로 설계되었다. 정상적인 제어 흐름에서 사용해서는 안 되며, 이를 프로그래머에게 가용하는 API를 만들어서도 안 된다.

70. 복구할 수 있는 상황에서는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라.

핵심 정리

복구할 수 있는 상황이면 검사 예외를, 프로그래밍 오류라면 비검사 예외를 던지자.
확실하지 않다면 비검사 예외를 던지자. 검사 예외도 아니고 런타임 에외도 아닌 throwable은 정의하지도 말자.
검사 예외라면 복구에 필요한 정보를 알려주는 메서드도 제공하자.

71. 필요없는 검사 예외 사용은 피하라.

핵심 정리

꼭 필요한 곳에만 사용한다면 검사 예외는 프로그램의 안전성을 높여주지만, 남용하면 쓰기 고통스러운 API를 초래한다.
API 호출자가 예외 상황에서 복구할 방법이 없다면 비검사 예외를 던지자. 복구가 가능하고 호출자가 그 처리를 해주길 바란다면, 우선 옵셔널을 반환해도 될지 고민하자.
옵셔널만으로는 상황을 처리하기에 충분한 정보를 제공할 수 없을 때만 검사 예외를 던지자.

72. 표준 예외를 사용하라.

핵심 정리

예외도 마찬가지로 재사용하는 것이 좋으며, 자바 라이브러리는 대부분 API에서 쓰기에 충분한 수의 예외를 제공한다.
하지만 Exception, RuntimeException, Throwable, Error는 직접 재사용하지 말자. 이 클래스들은 추상 클래스라고 생각하자.
이 예외들은 다른 예외들의 상위 클래스이므로, 즉 여러 성격의 예외들을 포괄하는 클래스이므로 안정적으로 테스트 할 수 없다.

널리 재사용되는 예외
예외 주요 쓰임
IllegalArgumentException 허용하지 않는 값이 인수로 전달 됬을 때
IllegalStateException 객체가 메서드를 수행하기 적절하지 않을 때
NullPointerException null을 허용하지 않는 메서드에 null을 건넸을 때
IndexOutOfBoundsException 인덱스가 범위를 넘었을 때
ConcurrentModificationException 허용하지 않는 동시 수정이 발견됬을 때
UnsupportedOperationException 호출한 메서드를 지원하지 않을때

73. 추상화 수준에 맞는 예외를 던져라.

핵심 정리

아래 계층의 예외를 예방하거나 스스로 처리할 수 없고, 그 예외를 상위 계층에 그대로 노출하기 곤란하다면 예외 번역을 사용하라.
이때 예외 연쇄를 사용하면 상위 계층에는 맥락에 어울리는 고수준 예외를 던지면서 근본 원인도 함께 알려주어 오류를 분석하기에 좋다.

74. 메서드가 던지는 모든 예외를 문서화하라.

핵심 정리

메서드가 던질 가능성이 있는 모든 예외를 문서화하라.
검사 예외든 비검사 예외든, 추상 메서드든 구체 메서드든 모두 마찬가지다. 문서화에는 자바독의 @throws 태그를 사용하면 된다.
검사 예외만 메서드 선언의 throws 문에 일일이 선언하고, 비검사 예외는 메서드 선언에는 기입하지 말자.
발생 가능한 예외를 문서로 남기지 않으면 다른 사람이 그 클래스나 인터페이스를 효과적으로 사용하기 어렵거나 심지어 불가능할 수도 있다.

75. 예외의 상세 메시지 실패 관련 정보를 담아라.

핵심 정리

예외를 잡지 못해 프로그램이 실패하면 그 예외의 스택 추적 정보를 자동으로 출력한다.
이 정보를 보다 상세하게 해준다면, 실패한 순간의 상황을 정확히 포착할 수 있다. 실패원인을 쉽게 분석하려면 발생한 예외의 관여된 모든 매개변수와 필드의 값을 실패 메시지에 담아야 한다.

76. 가능한 한 실패 원자적으로 만들라.

핵심 정리

호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지한다면, 그 객체는 여전히 정상적으로 사용할 수 있는 상태일 것이다.
객체를 원자적으로 만들기 위해서는

  1. 불변 객체로 설계하라.
  2. 작업 수행에 앞서 매개 변수의 유효성을 검사하라.
    public Object pop() {
     // 메서드 호출하기 전 매개변수의 유효성을 검사함으로써, 실패할 경우 상태 변경을 막는다.
     if(size == 0)
           throw new EmptyStackException();
          
       Object result = elements[--size];
       elements[size] = null;
       return result;
    }
    
  3. 객체의 임시 복사본에서 작업을 수행한 다음, 작업이 성공적으로 완료되면 원래 객체와 교환하라.
  4. 복구 코드를 작성하여 작업 전 상태로 되돌리라.(드문 케이스)

77. 예외를 무시하지 말라.

핵심 정리

너무 뻔한 조언 같지만 반복해 각인시켜야 할 정도 사람들이 자주 어기고 있다.

예외는 문제 상황에 잘 대처하기 위해 존재하는데, catch 블록을 비워두면 예외가 존재할 이유가 없어진다.

API 설계자가 메서드 선언에 예외를 명시하는 까닭은, 그 메서드를 사용할 때 적절한 조치를 취해달라고 말하는 것이다.