[이펙티브자바] 10장 예외(item69-77) 정리
아이템 69. 예외는 진짜 예외 상황에만 사용하라
- 예외를 제어 흐름용으로 사용하지 말자
- 예외를 제어 흐름에 사용하는 것은 표준 관용구보다 훨씬 느리다
- 예외를 제어 흐름에 사용하면, 흐름 제어에 사용한 예외가 버그를 숨겨 디버깅이 어려워진다
- 예외는 오직 예외 처리 용도로만 사용하자
아이템 70. 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라
- 호출하는 쪽에서 복구하리라 생각되는 경우엔 검사 예외를 사용
catch로 잡아 처리하거나, throws를 사용하여 더 바깥으로 전파하도록 강제
검사 예외라면 복구하는 법을 알려주는 메서드도 같이 제공하자
- 프로그래밍 오류를 나타낼 때는 런타임 예외를 사용
클라이언트가 해당 API의 명세를 지키지 않았을 때 사용
- 복구 가능하다고 믿으면 검사 예외, 그렇지 않으면 런타임 예외 사용하자
확신이 없어도 런타임 예외
- 직접 구현하는 비검사 throwable의 경우, RuntimeException의 하위 클래스여야 한다
- 이상한 것 좀 만들지 말자
아이템 71. 필요 없는 검사 예외 사용은 피하라
- 검사 예외는 api를 불편하게 만들 수 있다
검사 예외를 던지는 메서드는 스트림 안에서 직접 사용할 수 없음
- 검사 예외를 회피하는 방법
1. 비검사 예외 사용
2. Optional 반환
검사 예외 대신 빈 옵셔널 반환. 예외가 발생한 이유를 제공할 수 없음
3. 메서드 분리
검사 예외를 던지는 메서드 → 상태 검사 메서드 + 비검사 예외를 던지는 메서드
상태 검사 메서드는 외부 동기화 없이 여러 스레드가 동시에 접근할 수 있거나, 외부 요인에 의해 상태가 변할 수 있다면 사용하지 않는 것이 좋다 (아이템 69)
아이템 72. 표준 예외를 사용하라
- 표준 예외를 쓰자.
모두에게 쉽게 다가온다. 읽기 쉽다.
예외 클래스가 적을수록 메모리 사용량도 줄고, 클래스를 적재하는 시간도 적게 든다.
IllegalArgumentException
: 허용하지 않는 값이 인수로 건네졌을 때
IllegalStateException
: 객체가 메서드를 수행하기에 적절하지 않은 상태일 때
NullPointerException
: null을 허용하지 않는 메서드에 null을 건넸을 때
IndexOutOfBoundsException
: 인덱스가 범위를 넘었을 때
ConcurrentModificationException
: 허용하지 않는 동시 수정
UnsupportedOperationException
: 호출한 메서드를 지원하지 않을 때
- Exception, RuntimeException, Throwable, Error는 직접 재사용하지 말자.
추상 클래스라고 생각하자. 안정적으로 쓸 수 없다.
- 진짜 필요한 거 아니면 커스텀 예외는 만들지 말자
- 인수값이 무엇이었든 실패했을거라면 IllegalStateException, 그게 아니면 IllegalArgumentException
아이템 73. 추상화 수준에 맞는 예외를 던지라
- 상위 계층에서는 저수준 예외를 잡아 자신의 추상화 수준에 맞는 예외로 바꿔 던져야 한다. (예외 번역)
그렇지 않으면, 내부 구현 방식을 드러내어 윗 레벨 API를 오염시킨다. 구현 방식을 바꾸면 다른 예외가 튀어나와 기존 클라이언트 프로그램을 깨지게 할 수 있다.
- 예외 번역 시, 저수준 예외가 디버깅에 도움이 된다면 저수준 예외를 고수준 예외에 같이 실어보내는 예외 연쇄를 사용하자.
대부분의 표준 예외는 예외 연쇄용 생성자를 갖추고 있다.
- 그렇다고 예외 번역을 남용하지는 말자.
1. 저수준 메서드를 반드시 성공하게 해서 아래 계층에서 예외가 발생하지 않도록 (ex. 매개변수 넘기기 전에 미리 검사)
2. 적절히 로깅하여 예외 전파없이 적절한 조치를 취할 수 있도록
아이템 74. 메서드가 던지는 모든 예외를 문서화하라
- 검사 예외는 따로따로 선언, 모든 예외는 자바독 @throws로 정확히 문서화하자
공통 상위 클래스 하나로 뭉뚱그려버리면 안된다. JVM만이 호출할 수 있는 main 메서드는 예외
- 비검사 예외는 메서드 선언의 throws 목록에 넣지 말자
- 한 클래스 안의 많은 메서드가 같은 이유로 같은 예외를 던진다면, 클래스 설명에 추가할 수도 있다
아이템 75. 예외의 상세 메시지에 실패 관련 정보를 담으라
- 예외의 toString 메서드에 실패 원인에 관한 정보를 가능한 많이 담자
- 실패 순간을 포착하려면 발생한 예외에 관여된 모든 매개변수, 필드의 값을 실패 메세지에 담아야 한다
- 예외의 상세 메세지와 사용자에게 보여줄 오류 메세지를 혼동하지 말자
사용자에게 보여주는 것은 친절하게, 번역까지.
예외 메세지는 필요한 정보를.
아이템 76. 가능한 한 실패 원자적으로 만들라
- 호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지해야 한다.
- 메서드를 실패 원자적으로 만드는 방법
1. 불변 객체로 설계
불변 객체는 태생이 실패 원자적
가변 객체 사용한다면 작업 수행 전 매개변수 유효성 검사
2. 실패할 가능성이 있는 모든 코드를 상태를 바꾸는 코드보다 앞에 배치
3. 객체의 임시 복사본에서 작업 수행 → 작업 성공하면 원래 객체와 교체
4. 작업 도중 발생하는 실패를 가로채는 복구 코드 작성
- Error는 복구할 수 없으므로 실패 원자적으로 만들지 않아도 됨
- 항상 실패 원자적으로 만들어야하는 것은 아님
실패 원자성을 달성하기 위한 비용이나 복잡도가 아주 크다면..
아이템 77. 예외를 무시하지 말라
- catch 블록을 비워두지 말자
- 예외를 무시하기로 했다면, catch 블록 안에 주석 남기고, 예외 변수의 이름도 ignored로 바꾸자
- 무시하지 않고 바깥으로 전파되게만 둬도 최소한 디버깅 정보를 남긴 채 신속히 중단되게 할 수 있다