📌 예외 번역(Exception Translation)
💡 상위 계층에서는 저수준 예외를 잡아 자신의 추상화 수준에 맞는 예외로 바꿔 던져야 한다.
try {
... // 저수준 추상화를 이용한다.
} catch (LowerLevelException e) {
throw new HigherLevelException(...); // 추상화 수준에 맞게 번역한다.
}
- 메서드가 저수준 예외를 처리하지 않고 바깥으로 전파하면, 내부 구현 방식을 드러내 윗 레벨 API를 오염시킨다.
- 다음 릴리즈에서 구현 방식을 바꾸면 다른 예외가 튀어나와서 기존 Client 프로그램을 깨지게 할 수도 있다.
🟡 AbstractSequentialList
- AbstractSequentialList는 List의 골격 구현(Item 20)이다.
- 여기서 수행한 예외 번역은 List<E> 인터페이스의 get 메서드 명세에 명시된 필수 사항이다.
📌 예외 연쇄(Exception Chaining)
try {
... // 저수준 추상화 이용
} catch (LowerLevelException cause) {
throw new HigherLevelException(cause); // 저수준 예외를 고수준 예외에 실어 보냄
}
- 저수준 예외가 디버깅에 도움이 된다면 사용하라
- 문제의 근본 원인(cause)인 저수준 예외를 고수준 예외에 실어 보내는 방식이다.
- 필요하다면 별도의 접근자 메서드(Throwable의 getCause 메서드)를 통해 꺼내볼 수 있다.
- 고수준 예외의 생성자는 상위 클래스 생성자에 원인을 넘겨, Throwable(Trowable) 생성자까지 건네지게 한다.
🟡 예외 연쇄용 생성자
class HigherLevelException extends Exception {
HigherLevelException(Throwable cause) {
super(cause);
}
}
- 대부분의 표준 예외는 예외 연쇄용 생성자를 갖추고 있다. (없는 것도 있긴 하니 주의)
- 그렇지 않은 예외도 Throwable의 initCause() 메서드로 '원인'을 직접 못박을 수도 있다.
class Foo {
public static void main(String[] args) throws Exception
{
try {
testException1();
} catch (Throwable e) {
// Cause : java.lang.ArrayIndexOutOfBoundsException
System.out.println("Cause : " + e.getCause());
}
}
public static void testException1() throws Exception
{
ArrayIndexOutOfBoundsException ae = new ArrayIndexOutOfBoundsException();
Exception ioe = new Exception();
ioe.initCause(ae);
throw ioe;
}
}
📌 주의 사항
💡 무턱대고 예외를 전파하는 것보다는 낫지만, 그렇다고 예외 번역을 남용하진 마라
- 가능한 저수준 메서드가 반드시 성공하도록 하여, 예외 자체가 일어나지 않게 하는 것이 최선이다.
- 상위 계층 메서드 매개변수 값을 아래 계측 메서드로 건네기 전에 미리 검사하는 방법도 있다.
- 아래 계층의 예외를 피할 수 없다면 상위 계층에서 예외를 조용히 처리할 수도 있다.
- 이 경우에는 적절한 Logging 기능을 활용해 기록해두면 좋다.
- Client에 문제를 전파하지 않으면서, 프로그래머가 로그를 통해 추가 조치를 취할 수 있게 돕는다.