티스토리 뷰

  • 아이템 69. 예외는 진짜 예외 상황에만 사용하라
  • 아이템 70. 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라
  • 아이템 71. 필요 없는 검사 예외 사용은 피하라
  • 아이템 72. 표준 예외를 사용하라
  • 아이템 73. 추상화 수준에 맞는 예외를 던지라
  • 아이템 74. 메서드가 던지는 모든 예외를 문서화하라
  • 아이템 75. 예외의 상세 메시지에 실패 관련 정보를 담으라
  • 아이템 76. 가능한 한 실패 원자적으로 만들라
  • 아이템 77. 예외를 무시하지 말라

 

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

  • 예외는 오직 예외 상황에서만 써야 하고 절대로 일상적인 제어 흐름용으로 쓰여선 안 된다.
  • 특정 상태에서만 호출할 수 있는 '상태 의존적' 메서드를 제공하는 클래스는 '상태 검사' 메서드도 함께 제공해야 한다.
    ex) Iterator 인터페이스의 next와 hasNext
  • 상태 검사 메서드 대신 올바르지 않은 상태일 때 빈 옵셔널 혹은 null 같은 특수한 값을 반환하는 방법도 있다.
  • 외부 동기화 없이 여러 스레드가 동시에 접근할 수 있거나 외부 요인으로 상태가 변할 수 있다면 옵셔널이나 특정 값을 사용한다.
  • 성능이 중요한 상황에서 상태 검사 메서드가 상태 의존적 메서드의 작업 일부를 중복 수행한다면 옵셔널이나 특정 값을 선택한다.
  • 다른 모든 경우엔 상태 검사 메서드 방식이 조금 더 낫다. 가독성이 더 좋기 때문이다.

 

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

  • 호출하는 쪽에서 복구하리라 여겨지는 상황이라면 검사 예외를 사용하라.
  • 비검사 throwable은 두 가지, 런타임 예외와 에러다. 프로그램에서 비검사 예외나 에러를 던졌다는 것은 복구가 불가능하거나 더 실행해봐야 득보다는 실이 많다는 뜻이다.
  • 프로그래밍 오류를 나타낼 때는 런타임 예외를 사용하자.
  • 구현하는 비검사 throwable은 모두 RuntimeException의 하위 클래스여야 한다.
  • 검사 예외는 일반적으로 복구할 수 있는 조건일 때 발생하므로 호출자가 예외 상황에서 벗어나는 데 필요한 정보를 알려주는 메서드를 함께 제공하는 것이 중요하다.

 

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

  • 검사 예외가 단 하나뿐이라면 오직 그 예외 때문에 API 사용자는 try 블록을 추가해야 하고 스트림에서 직접 사용하지 못하게 된다. 그러니 이런 상황이라면 검사 예외를 안 던지는 방법이 없는지 고민해볼 가치가 있다.
  • 검사 예외를 회피하는 가장 쉬운 방법은 적절한 결과 타입을 담은 옵셔널을 반환하는 것이다.
  • 반면, 예외를 사용하면 구체적인 예외 타입과 그 타입이 제공하는 메서드들을 활용해 부가 정보를 제공할 수 있다.

 

아이템 72. 표준 예외를 사용하라

  • 표준 예외를 재사용하면 얻는 게 많다. 그중 최고는 여러분의 API가 다른 사람이 익히고 사용하기 쉬워진다는 것이다.
  • 여러분의 API를 사용한 프로그램도 낯선 예외를 사용하지 않게 되어 읽기 쉽게 된다는 장점도 크다.
  • 예외 클래스 수가 적을수록 메모리 사용량도 줄고 클래스를 적재하는 시간도 적게 걸린다.
예외 주요 쓰임
IllegalArgumentException 허용하지 않는 값이 인수로 건네졌을 때(null은 따로 NullPointerException으로 처리)
IllegalStateException 객체가 메서드를 수행하기에 적절하지 않은 상태일 때
NullPointerException null을 허용하지 않는 메서드에 null을 건넸을 때
IndexOutOfBoundsException 인덱스가 범위를 넘어섰을 때
ConcurrentModificationException 허용하지 않는 동시 수정이 발견됐을 때
UnsupportedOperationException 호출한 메서드를 지원하지 않을 때
  • 인수 값이 무엇이었든 어차피 실패했을 거라면 IllegalStateException을, 그렇지 않으면 IllegalArgumentException을 던지자.

 

아이템 73. 추상화 수준에 맞는 예외를 던지라

  • 상위 계층에서는 저수준 예외를 잡아 자신의 추상화 수준에 맞는 예외로 바꿔 던져야 한다.
  • 예외 연쇄란 문제의 근본 원인인 저수준 예외를 고수준 예외에 실어 보내는 방식이다.
  • 고수준 예외의 생성자는 상위 클래스의 생성자에 원인을 건네주어, 최종적으로 Throwable 생성자까지 건네지게 한다.
  • Throwable의 initCause 메서드를 이용해 원인을 직접 못박을 수 있다.
  • 가능하다면 저수준 메서드가 반드시 성공하도록하여 아래 계층에서는 예외가 발생하지 않도록 하는 것이 최선이다.

 

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

  • 검사 예외는 항상 따로따로 선언하고, 각 예외가 발생하는 상황을 자바독의 @throws 태그를 사용하여 정확히 문서화하자. 공통 상위 클래스 하나로 뭉뚱그려 선언하는 일은 삼가자.
  • 메서드가 던질 수 있는 예외를 각각 @throws 태그로 문서화하되, 비검사 예외는 메서드 선언의 throws 목록에 넣지 말자. 검사냐 비검사냐에 따라 API 사용자가 해야 할 일이 달라지므로 이 둘을 확실히 구분해주는 게 좋다.

 

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

  • 실패 순간을 포착하려면 발생한 예외에 관여된 모든 매개변수와 필드의 값을 실패 메시지에 담아야 한다.
  • 보안과 관련한 정보는 주의해서 다뤄야 한다. 문제를 진단하고 해결하는 과정에서 스택 추적 정보를 많은 사람이 볼 수 있으므로 상세 메시지에 비밀번호나 암호 키 같은 정보까지 담아서는 안 된다.
  • 예외의 상세 메시지와 최종 사용자에게 보여줄 오류 메시지를 혼동해서는 안 된다. 최종 사용자에게는 친절한 안내 메시지를 보여줘야 하는 반면, 예외 메시지는 가독성보다는 담긴 내용이 훨씬 중요하다.


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

  • 호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지해야 한다. 이러한 특성을 실패 원자적(failure-atomic)이라고 한다.
  • 가장 간단한 방법은 불변 객체로 설계하는 것이다.
  • 가변 객체의 메서드를 실패 원자적으로 만드는 가장 흔한 방법은 작업 수행에 앞서 매개변수의 유효성을 검사하는 것이다.
  • 실패할 가능성이 있는 모든 코드를, 객체의 상태를 바꾸는 코드보다 앞에 배치하는 방법도 있다.
  • 객체의 임시 복사본에서 작업에서 작업을 수행한 다음, 작업이 성공적으로 완료되면 원래 객체와 교체하는 방법도 있다.
  • 작업 도중 발생하는 실패를 가로채는 복구 코드를 작성하여 작업 전 상태로 되돌리는 방법도 있다.

 

아이템 77. 예외를 무시하지 말라

  • 예외를 무시하기로 했다면 catch 블록 안에 그렇게 결정한 이유를 주석으로 남기고 예외 변수의 이름도 ignored로 바꿔놓도록 하자.

 

댓글