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

728x90

(그런 사람은 없겠지만, 설레발좀 치겠다) 꾸준히 이 블로그를 눈팅해왔던 사람이라면, 엥? 중간은 어디가고 갑자기 아이템 72인가 하겠다. 이 부분은 내가 피드백 받았던 부분이었는데(상황을 간략히 요약하자면, 무분별한 사용자 정의 예외 사용에 대해서 피드백 받았던 부분이다), 봐도봐도 기억에 남지 않아, 글로 남기고자 한다.

표준 예외를 사용하라

자바 라이브러리는 대부분 API에서 쓰기에 충분한 수의 예외를 제공한다. 표준 예외를 재사용하면 얻는 게 많다. 당연 그중에 가장 유익한 점은 1) 우리의 API가 다른 사람에게 쉽게 다가온다는 점이다(이미 많은 프로그래머에게 익숙해진 규약을 그대로 따르기 때문이다). 2. 우리의 API도 낯선 예외를 사용하지 않게 되어 읽기 쉽게 된다는 장점도 크다. 3) 마지막으로, 예외 클래스 수가 적을수록 메모리 사용량도 줄고 클래스를 적재하는 시간도 적게 걸린다.

먼저 간략하게 널리 재사용되는 예외들을 표로 정리했다.

예외주요 쓰임
IllegalArgumentException허용하지 않는 값이 인수로 건네졌을 때 (주의* null은 따로 NullPointerException으로 처리)
IllegalStateException객체가 메서드를 수행하기에 적절하지 않은 상태일 때
NullPointerExceptionnull을 허용하지 않는 메서드에 null을 건넸을 때 
IndexOutOfBoundsException인덱스가 범위를 넘어섰을 때
ConcurrentModificationException허용하지 않는 동시 수정이 발견됐을 때 
UnsupportedOperationException호출한 메서드를 지원하지 않을 때 

조금 더 자세히 설명을 덧붙여본다.

  • IllegalArgumentException
    가장 많이 재사용되는 예외로(아이템 49), 호출자가 인수로 부적절한 값을 넘길 때 던지는 예외다. (ex. 반복 횟수를 지정하는 매개변수에 음수가 할당될 때)

  • IllegalStateException
    이또한 자주 재사용된다. 이 예외는 대상 객체의 상태가 호출된 메서드를 수행하기에 적합하지 않을 때 주로 던진다. 제대로 초기화되지 않은 객체를 사용하려 할 때 던질 수 있다.

  • NullPointerException
    메서드가 던지는 모든 예외를 잘못된 인수나 상태라고 포괄적으로 생각할 수도 있어, IllegalArgument라고 볼 수도 있겠으나, 그 중 특수한 일부는 따로 구분해서 써야한다. null 값을 허용하지 않는 메서드에 null을 건네면 관례상 NullPointerException을 던진다.

  • IndexOutOfBoundsException
    NullPointerException과 유사하게 특수한 예로, 어떤 시퀀스의 허용 범위를 넘는 값을 건넬 때, IndexOutOfBoundsException을 던진다.

  • ConcurrentModificationException
    단일 스레드에서 사용하려고 설계한 객체를 여러 스레드가 동시에 수정하려 할 때 던진다. (사실 동시 수정을 확실히 검출할 수 있는 안정된 방법은 없어, 이 예외는 문제가 생길 가능성을 알려주는 정도의 역할로 쓰인다)

  • UnsupportedOperationException
    이 예외는 클라이언트가 요청한 동작을 대상 객체가 지원하지 않을 때 던진다. 대부분 객체는 자신이 정의한 메서드를 모두 지원하니 흔히 쓰이는 예외는 아니다. 보통은 구현하려는 인터페이스의 메서드 일부를 구현할 수 없을 때 쓰는데, 예를 들어 원소를 넣을 수만 있는 List 구현체에 대고 누군가 remove 메서드를 호출하면 이 예외를 던질 것이다.

Exception, RuntimeException, Throwable, Error는 직접 재사용하지 말자.

이 클래스들은 추상 클래스라고 생각하길 바란다. 이 예외들은 다른 예외들의 상위 클래스이므로, 즉 여러 성격의 예외들을 포괄하는 클래스이므로 안정적인 테스트가 불가능하다.

지금까지 언급한 것들이 가장 흔하게 재사용되는 예외이다. 이 외에도 다른 예외도 재사용할 수 있다. 복소수나 유리수를 다루는 객체를 작성한다면 ArithmeticException이나 NumberFormatException을 재사용할 수 있을 것이다. 상황에 부합한다면 항상 표준 예외를 재사용하자. 이때 API 문서를 참고해 그 예외가 어떤 상황에서 던져지는지 꼭 확인해야 한다. 예외의 이름뿐 아니라 예외가 던져지는 맥락도 부합할 때만 재사용한다. 더 많은 정보를 제공하길 원한다면 표준 예외를 확장해도 좋다. 단, 예외는 직렬화할 수 있다는 사실을 기억하자(12장). 직렬화에는 많은 부담이 따르기 때문에 이 사실만으로 굳이 사용자 정의 예외를 새로 만들지 않아야 할 근거로 적절해 보인다.

728x90