생각해보기
이펙티브 자바 -9- 본문
예외
예외를 효과적으로 활용하는 지침
예외는 진짜 예외 상황에서만 사용하라
예외는 오직 예외 상황에서만 써야한다. 절대로 일상적인 제어 흐름용으로 쓰여서는 안된다. 표준적이고 쉽게 이해되는 관용구를 사용하라.
예외는 예외 상황에 쓸 용도로 설계되었으므로 최적화에 별로 신경 쓰지 않았을 가능성이 크다. 또한 코드를 try-catch 블록 안에 넣으면 JVM이 적용할 수 있는 최적화가 제한 된다.
잘 설계된 API라면 클라이언트가 정상적인 제어 흐름에서 예외를 사용할 일이 없게 해야 한다. 특정 상태에서만 호출할 수 있는 '상태 의존적' 메서드를 제공하는 클래스는 '상태 검사' 메서드도 함께 제공해야 한다. 예를 들어 Iterator 인터페이스의 next와 hasNext가 각각 상태 의존적 메서드와 상태 검사 메서드에 해당된다.
상태 검사 메서드 대신 올바르지 않은 상태일때 null이나 빈 옵셔널 같은 특수한 값을 반환할 수 있다.
상태 검사 메서드, 특수한 값 선택 지침
- 외부 동기화 없이 여러 스레드가 동시에 접근할 수 있거나 외부 요인으로 상태가 변할 수 있다면 옵셔널이나 특정 값을 사용한다. 상태 검사 메서드와 상태 의존적 메서드 사이에 상태가 변할 수 있기 때문이다
- 성능이 중요한 상황에서 상태 검사 메서드가 상태 검사 메서드가 상태 의존적 메서드의 작업 일부를 중복으로 사용한다면 옵셔널이나 특정 값을 선택한다
- 다른 모든 경우에는 상태 검사 메서드 방식이 조금 더 낫다. 잘못 사용했을 때 발견하기가 쉽다
정리
예외는 예외 상황에서 쓸 의도로 설계된것이다. 사용자에게 예외를 직접적으로 사용하게 하지 말고 상태 검사 메서드와 특수한 값을 통해 간접적으로 예외를 들어내라
복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라
자바는 문제 상황을 알리는 타입(throwable)으로 검사 예외(체크 예외), 런타임 예외, 에러를 제공한다.
호출하는 쪽에서 복구하리라 여겨지는 상황이라면 검사 예외를 사용하라. 검사 예외를 던지면 호출자가 그 예외를 잡거나 더 바깥으로 전파하도록 강제하게 된다. 달리 말하면 API 설계자들은 API 사용자들에게 검사 예외를 던져주어 그 상황에서 회복하라고 요구하는 것이다.
프로그래밍 오류를 나타날 때는 런타임 예외를 사용하라. 런타임 예외의 대부분은 전제조건을 만족하지 못했을 때 발생한다. 전제조건 위배란 단순히 클라이언트가 해당 API의 명세에 기록된 제약을 못지켰다는 것이다.
API 설계자는 회복이 가능하다고 믿는 다면 검사 예외를 그렇지 않다면 런타임 예외를 사용하자. 확신하기 어렵다면 런타임 예외를 선택하는 것이 낫다
애러는 JVM이 자원 부족, 불변식 깨짐 등 더 이상 수행을 계속할 수 없는 상황을 나타낼 때 사용한다. Error는 상속하지 말아야 할 뿐 아니라, throw 문으로 직접 던지는 일도 없어야 한다.
우리가 구현하는 비검사 throwable은 모두 RuntimeException의 하위 클래스여야 한다.
Exception은 주로 그 예외를 일으킨 상황에 관한 정보를 코드 형태로 전달하는데 쓰인다. 다시말하자면 Exception 클래스 명 자체에 정보가 있다. (Exception명 잘 만들자)
정리
복구할 수 있는 상황이면 검사 예외를, 프로그래밍 오류라면 비검사 예외를 던지자. 확실하지 않다면 비검사 예외를 던지자. 검사 예외라면 복구에 필요한 정보를 알려주는 메서드도 제공하자
필요 없는 검사 예외 사용은 피하라
검사 예외는 발생한 문제를 프로그래머가 처리하여 안전성을 높이게끔 해준다. 물론, 검사 예외를 과하게 사용하면 오히려 쓰기 불편한 api가 된다.
api를 제대로 사용하도 발생할 수 있는 예외이거나, 프로그래머가 의미 있는 조치를 취할 수 있는 경우라면 검사 예외를 사용해도 좋다. 그렇지 않다면 비검사 예외를 사용하는 것이 좋다
정리
꼭 필요한 곳에만 사용한다면 검사 예외는 프로그램의 안전성을 높여주지만, 남용하면 쓰기 불편한 api를 낳는다. api 호출자가 예외 상황에서 복구할 방법이 없다면 비검사 예외를 던지자. 복구가 가능하고 호출자가 그 처리를 해주길 바란다면 옵셔널을 고민하고, 옵셔널만으로 상황을 처리하기 충분한 정보를 제공할 수 없을 때 검사 예외를 던지자
표준 예외를 사용하라
우리 api가 다른 사람이 익히고 사용하기 쉬워진다. 다만 Exception, RuntimeException, Throwable, Error는 직접 재사용하지 말자. 추상적이기 때문에 무슨 에러 인지 테스트하기 어렵다
많이 쓰는 예외
- IllegalArgumemtException : 허용하지 않는 값이 인수로 건네졌을 때
- IllegalStateException : 객체가 메서드를 수행하기에 적절하지 않는 상태일때
- NullPointerException : null을 허용하지 않는 메서드에 null을 건넸을 때
- IndexOutOfBoundsException : 인덱스가 범위를 넘어섰을 때
- ConcurrentModificationException : 허용하지 않는 동시 수정이 발견 됐을 때
- UnsupportedOperationException : 호출할 메서드를 지원하지 않을 때
인수 값이 무엇이 었든 어차피 실패했을 거라면 IllegalStateException을, 그렇지 않다면 IllegalArgumemtException 을 던지자
상황에 부합한다면 항상 표준 예외를 재사용하자
추상화 수준에 맞는 예외를 던지라
메서드가 저 수준의 예외를 처리하지 않고 바깥으로 전파해버리면 내부 구현 방식을 드러내어 윗 레벨 API를 오염시킨다. 이러한 문제를 피하려면 상위 계층에서는 저수준 예외를 잡아 자신의 추상화 수준에 맞는 예외로 바꿔 던져야 한다.
이를 예외 번역이라 한다.
정리
아래 계층의 예외를 예방하거나 스스로 처리할 수 없고, 그 예외를 상위 계층에 그대로 노출하기 곤란하다면 예외 번역을 사용하라.
메서드가 던지는 모든 예외를 문서화하라
검사 예외는 항상 따로따로 선언하고, 각 예외가 발생하는 상황을 자바독의 @throws 태그를 사용하여 정확히 문서화하자. 발생 가능한 예외를 문서로 남기지 않으면 다른 사람이 그 클래스나 인터페이스를 효과적으로 사용하기 어렵다
예외의 상세 메시지에 실패 관련 정보를 담으라
예외를 잡지 못해 프로그램이 실패하면 자바 시스템은 그 예외의 스택 추적 정보를 자동으로 출력한다. 스택 추척은 예외 객체의 toString 메서드를 호출해 얻는 문자열로, 보통은 예외의 클래스 이름 뒤에 상세 메서드가 붙는 형태이다. 따라서 실패 원인에 관한 정보를 가능한 많이 담아 반환하는 일은 아주 중요하다
실패 순간을 포착하려면 발생한 예외에 관여된 모든 매개변수와 필드의 값을 실패 메시지에 담아야 한다. 단 보안과 관련된 정보는 주의해야 한다. 상태 메시지에 비밀번호나 암호 키 같은 정보까지 담아서는 안된다
가능한 실패 원자적으로 만들라
호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지해야한다. 이러한 특성을 실패 원자적이라고 한다.
메서드를 실패 원자적으로 만드는 방법은 다양하다. 그중 가장 간단한 방법은 불변 객체로 설계하는 것이다. 불변 객체는 불안정한 상태로 빠지는 일이 없다. 불변 객체의 상태는 생성 시점에 고정되어 절대 변하지 않기 때문이다.
가변 객체의 메서드를 실패 원자적으로 만드는 가장 흔한 방법은 작업 수행에 앞서 매개변수의 유효성을 검사하는 것이다, 이와 비슷하게 실패할 가능성이 있는 모든 코드를, 객체의 상태를 바꾼느 코드보다 앞에 배치하는 방법도 있다
또다른 방법은 객체의 임시 복사본에서 작업을 수행한 다음, 작업이 성공적으로 완료되면 원래 객체와 교체하는 것이다.
예외를 무시하지 말라
예외는 문제 상황에 잘 대처하기 위해 존재하는 데 catch 블록을 비워두면 예외가 존재할 이유가 없어진다.
만약 예외를 무시하기로 했다면 catch 블록 안에 그렇게 결정한 이유를 주석으로 남기고 예외 변수의 이름도 ignored로 바꿔놓도록 하자
'자바' 카테고리의 다른 글
JVM에 대해서 알아보자 (0) | 2023.06.11 |
---|---|
이펙티브 자바 -11- (0) | 2022.01.17 |
이펙티브 자바 -8- (0) | 2022.01.14 |
이펙티브 자바 -7- (0) | 2022.01.13 |
이펙티브 자바 -6- (0) | 2022.01.12 |