연결된 예외(chained-exception)

정순동·2023년 12월 11일
0

자바기초

목록 보기
45/89

한 예외가 다른 예외를 발생시킬 수도 있다. A예외가 B예외를 발생시켰다면, A예외는 B예외의 '원인 예외(cause exception)'이라고 한다.

예시

	class ChainedTest {
    	try {
        	throw new CustomException1;	// 사용자 정의 예외 1 강제 발생
        } catch(CustomException1 c1) {
        	CustomException2 c2 = new CustomException2("예외 발생"); // 사용자 정의 예외 2 인스턴스 생성
            c2.initCause(c1);	// CustomException2 c2의 원인 예외를 CustomException1 c1으로 지정
            throw c2;	// CustomException2를 발생시킨다.
        }
    }

먼저 연결되어 발생할 예외를 생성하고, initCause()로 원인 예외의 인스턴스를 지정한다.
그 후 연결되어 발생할 예외를 throw로 발생시켜주면 된다.

initCause()는 Exception클래스의 조상인 Throwable클래스에 정의되어 있기에 모든 예외에서 사용가능하다.

Throwable initCause(Throwable cause)
지정된 예외를 원인 예외로 등록
Throwable getCause()
저장된 원인 예외를 반환

발생항 예외를 그냥 처리하면 될 텐데, 왜 원인 예외로 등록하여 다시 예외를 발생시키는지 궁금할 것이다.

그 이유는 여러가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위해서이다.

아래와 같이 상속관계로 자손을 포함하는 부모 예외를 catch하게 된다면 예외1이 발생했는지 예외2가 발생했는지 알 수 없는것도 문제이다.

	try {
    	throw ChildException1;
   	} catch (ParentException pe) {
    	pe.printStackTrace();
    }
    
    class ChildException1 extends ParentException {
    	ChildException1(String msg) {
        	super(msg);
        }
    }
    class ChildException2 extends ParentException {
    	ChildException2(String msg) {
        	super(msg);
        }
    }
    class ParentException extends Exception {
    	ParentException(String msg) {
        	super(msg);
        }
    }

이런 상황이 발생하는게 큰 문제이기 때문에 Throwable클래스에서 예외가 원인 예외를 포함할 수 있게 한 것이다.

  • throwable 클래스
	public class Throwable implements Serializable {
    	...
        private Throwable cause = this; // 원인을 객체 자신(this)로 초기화
        ...
    }

또 다른 이유는 checked예외를 unchecked예외로 바꿀 수 있도록 하기 위해서이다. 기존에 checked예외로 예외 처리를 강제한 이유는 프로그래밍 경험이 적은 사람도 보다 견고한 프로그램을 작성할 수 있도록 유도하기 위한 것이었는데, 지금은 자바가 처음 개발되던 1990년대와 컴퓨터 환경이 많이 달라졌다. 그래서 checked예외가 발생해도 예외를 처리할 수 없는 상황이 하나 둘 발생하기 시작했고 이럴 때 할 수 있는 일이라곤 try-catch문만 추가하는 것뿐인데, checked예외를 unchecked예외로 바꾸면 선택적이 되므로 억지로 예외처리를 하지 않아도 된다.

예시 (프로그램 설치 시 오류)

	static void startInsatall() throws SpaceException, MemoryException {
    	if(!enoughSpace())
        	throw new SpaceException("설치할 공간이 부족합니다.");
            
        if(!enoughMemory())
        	throw new MemoryException("메모리가 부족합니다.");
    }

위 코드를 아래와 같이 바꾸어 보겠다.

	static void startInstall() throws SpaceException {
    	if(!enoughSpace())
        	throw new SpaceException("설치할 공간이 부족합니다.");
            
        if(!enoughMemory())
        	throw new RuntimeException(new MemoryException("메모리가 부족합니다."));
    }

MemoryException은 Exception의 자손이므로 반드시 예외를 처리해야하는데, 이 예외를 RuntimeException으로 감싸버렸기 때문에 unchecked예외가 되었다. 그래서 더 이상 startInstall()에 MemoryException을 선언하지 않아도 된다.

위의 코드는 initCuase()대신 RuntimeException의 생성자를 활용했다

	RuntimeException(Throwable cause) // 원인 예외를 등록하는 생성자

0개의 댓글