표준 예외를 사용하라.
많이 쓰이는 예외
IllegalArgumentException
: 호출자가 인수로 부적절한 값을 넘길 때 던지는 예외.IllegalStateException
: 대상 객체의 상태가 호출된 메서드를 수행하기에 적절하지 않을 때NullPointerException
: null 값을 허용하지 않는 메서드에 null을 건낼 때IndexOutOfBoundsException
: 시퀀스의 허용 범위 넘을 때ConcurrentModificationException
: 단일 스레드에서 사용하려고 설계한 객체를 여러 스레드가 동시에 수정하려고 할 때UnsupportedOperationException
: 클라이언트가 요청한 동작을 대상 객체가 지원하지 않을 때 private Car getMaxPositionCar() {
return cars.stream()
.max(Car::compareTo)
.orElseThrow(() -> new IllegalArgumentException("차량이 존재하지 않습니다"));
...
}
IllegalArgumentException은 어떨 때 사용하는 예외일까요?
-> 인수를 받지 않았으므로 IllegalStateException
이 더 적절한 상황이다.
만약 카드 덱을 표현 하는 객체에서 인수로 뽑을 만큼 수를 주는데, 남아 있는 카드 수보다 크다면 어떤 예외?
인수 값이 무엇이든 실패했을 거라면 IllegalStateException
, 그렇지 않으면 IllegalArgumentException
.
Exception
, RuntimeExcpetion
, Throwable
, Error
는 직접 재사용하지 말자. 다른 예외의 상위 클래스이므로 안정적으로 테스트할 수 없다.
더 많은 정보를 제공하기를 원한다면 표준 예외를 확장해도 좋은데, 예외는 직렬화 할 수 있다. (12장).
직렬화는 많은 부담이 따르니 이것만으로도 커스텀 예외를 만들지 않을 이유가 된다.
직렬화: 객체를 데이터 스트림으로 만드는 것. 객체를 저장하거나 전송하려면 당연히 해야 한다.
하지만 타입 정보등의 클래스 메타 정보를 포함하기 때문에 성능이 안좋아지고, 역직렬화를 통해 클래스 안의 모든 타입의 객체를 만들어 그 타입 안의 코드 수행 가능해짐. 위험해진다.
IllegalArgumentException
-> RuntimeException
-> Exception
-> Throwable
-> Serializable
예외 이름 자체가 정보를 전달할 수 있다.
NoSuchElementException
보다는 PostNotFoundException
이 더 정확하다.
더 상세하게 예외 정보를 제공할 수 있다.
public class IllegalIndexException extends IndexOutOfBoundsException {
private static final String message = "범위를 벗어났습니다.";
public IllegalIndexException(List<?> target, int index) {
super(message + " size: " + target.size() + " index: " + index);
}
}
예외의 응집도 향상
예외에 필요한 메시지, 전달할 정보의 데이터 등등을 한 곳에서 관리가 가능하다.
정확하게 위치 파악이 가능하다.
커스텀화를 시켰기 때문에 어떤 상황에서 발생했는지 특정할 수 있다.
예외 생성 비용을 절감한다.
예외를 생성할 때 stackTrace
때문에 비용이 많이 발생한다. 이 stackTrace
는 부모클래스 중 Throwable.fillInStackTrace()
에서 발생하는데, 이걸 Override해서 간략하게 하거나 아예 생성하지 않을 수 있다.