연결된 예외
연결된 예외 (chained exception)
- 한 예외가 다른 예외를 발생시킬 수 있다.
- 예외 A가 예외 B를 발생시키면, A는 B의 원인 예외(cause exception)
어떤 한 예외가, 다른 예외를 발생시킬 수 있고,
예를 들어서 예외 A가 예외 B를 발생시키면, A는 B의 원인 예외(cause exception)이라고 한다.
즉, 예외 A는 예외 B가 발생하게 된 원인이라는 뜻이다.
이 예외 A와 예외 B를 연결하는 것을 연결된 예외라고 하는데,
이 두 예외를 연결시키는데 사용되는 메서드는 initCause 라고 하는데, 매개변수로 예외를 지정해주면 된다.
그러면, 매개변수로 지정한 예외가 원인 예외로 등록이 된다.
그리고, 이 지정된 원인예외를 얻을 때는,
getCause()라는 메서드를 사용하면 된다. 그러면, 어떤 예외의 원인 예외를 얻을 수 있다.
코드를 보자
Throwable이라는 클래스가 있는데, Throwable은 Exception과 Error의 조상이다.
그래서 Throwable이지만, Exception클래스라고 생각하고 보면 된다.
이 클래스 내부에 원인 예외를 저장할 수 있는 필드(iv) cause가 있는데,
처음에 초기값은, 자기 자신을 원인 예외로 등록을 해놓는다.
initCause메서드에 예외 A를 매개변수로 지정해주면,
매개변수에 지정한 예외 A를 this.cause iv에 저장한다.
그래서 cause에 예외 A가 저장된다.
하나의 예외 안에 또다른 예외를 포함 시키는 것이다.
예외안에 또다른 예외를 포함시키는 것이 연결된 예외이다.
예를 들어서, Install이라는 메서드가 있을 때,
startInstall()이라는 메서드를 통해서 install을 시작할 준비를 하는데,
여기서 SpaceException 예외A가 발생했다. (저장공간 부족)
이것을, catch블럭이 처리를 하는데,
catch블럭에서 새로운 예외B가 만들어진다.
ie.initCause(e); 메서드는, 예외 A를 예외 B의 원인 예외로 등록을 해주는 것이다. (ie는 예외 B고, e는 예외 A)
즉, try문에서 SpaceException이 발생하면, InstallException을 만든다.
그래서 SpaceException(예외 A)를 InstallException(예외 B)에 포함시킨 것이다.
이때, 사용하는 메서드가initCause()이고, 이 메서드를 사용해서 포함시키는 것이다.
실제로 발생한 것은 SpaceException인데, InstallException을 만들어서 그 안에 넣어준 것이다. (포함시킨 것이다.)
그리고, 이 두 예외를 연결된 예외라고 한다.
이런식으로 실제로 발생된 예외를 새로운 예외에 포함시킨다음에, throw를 이용해서 다시 던진다.
그래서, install()메서드에 InstallException을 선언한 것이다.
원래는 실제 발생한 예외가 SpaceException이므로 SpaceException을 선언해야 하지만,
새로 만든 InstallException이라고 쓴다.
InstallException에는 SpaceException을 포함시키고,
이것을 thorw로 던졌기 때문에, install()메서드에서도 InstallException을 선언한 것이다.
이런 방식을 연결된 예외라고 한다.
연결된 예외를 사용하는 이유
[이유 1] 여러 예외를 하나로 묶어서 다루기 위해서
예를 들어서, 프로그램을 설치하다가, 메모리공간부족 예외인 SpaceException이 발생했다.
e.printStackTrace()가 아래의 결과를 출력한 것이다.
그런데, 코드를 보면, 예외가 SpaceException, MemoryException 2개가 있다.
설치하다가 발생할 수 있는 에러는 실제로는 더많을 수 도 있다.
그런데, 이게 너무 많으면, catch블럭이 많아진다는 단점이 있다.
그러면, install()이라는 메서드를 사용할 때마다,
많은 catch블럭을 쓰는 것이 부담스러울 것이다.
그래서 이것을 하나의 catch블럭으로 만들때 연결된 예외를 사용하는 것이다.
연결된 예외를 사용하려면, catch블럭들을, install메서드 안으로 집어넣어야 한다.
그리고install()메서드가 설치를 시작하고 예외가 발생하면, 그 예외를 InstallException안에다가 넣어주는 것이다.
즉, InstallException을 만들고, 이 안에다가 실제 발생한 예외(원인예외)를 넣는 것이다. (SpcaeException, MemoryException)
그러고나서 이 이 예외를 호출한 메서드인 install에게 thorw로 던지는 것이다.
그래서 install메서드에 installException예외를 선언해야 하는것이다.
이렇게 연결된 예외를 사용하면, 아까 전에 install메서드를 호출한 쪽에서 catch블럭을 여러개 써야했던 것과 달리,
catch블럭에 InstallException만 써주면 된다.
세부적인 예외처리는 InstallException안에 집어넣고, 실제 예외가 발생했을 때, InstallException만 던지도록 선언한 것이다.
InstallException을 던지는 쪽은, 예외를 처리한 것이 아니다. InstallException과 SpaceException예외를 연결하는 작업만 해주는 것이다.
MemoryException의 catch문에도 마찬가지로 InstallException과 연결해주는 코드를 작성하면 된다.
우측코드처럼 연결된 예외를 사용해서 코드를 작성해서 호출하면,
InstallException이 발생하게 된다. SpaceException이나, MemoryException이 아니라, InstallException이 발생한다.
호출한 쪽에 InstallException을 처리하게 되어있으니, InstallException만 catch블럭에 넣으면 된다.
InstallException이 발생했을 때, printStackTrac()를 출력하게 되면,
위와 같은 결과를 얻을 수 있다.
설치중 예외가 발생했는데,
SpaceException 즉, 설치할 공간이 부족하다는 에러가 발생했다는 것을 알 수 있다.
SpcaeException이 원인예외이다.
연결된 예외를 사용하지 않은 좌측 코드의 결과와 비교해보면,
연결된 예외를 사용했을 때, 결과가 더 자세하다.
SpaceException이 발생한 대략적인 정보를 얻을 수 있기 때문이다.
SpaceException은 install말고도 다른 곳에도 사용될 수 있는데, 간이 부족하다는 (구체적인 정보)정보만 나오는 것 보다는,
설치를 하다가 공간이 부족하다 라고 알려주는게 좀더 정보전달이 잘 된다.
그리고, 처리하는 쪽에서도 세부적인 catch처리를 메서드 블럭으로 감출 수 있어서 예외처리가 간단해진다.
이러한 장점들이 존재하기 때문에, 연결된 예외를 사용한다.
[이유 2] checked예외를 unchecked예외로 변경하려 할 때
연결된 예외를 사용하는 두번째 이유는,
checked예외를 unchecked예외로 변경하려 할 때 사용한다.
checked예외는 Exception의 자손이고, 필수처리대상이다.
Unchecked예외는 RuntimeException의 자손이고, 선택처리 대상이다.
즉, 연결된 예외는 필수처리를 선택처리로 바꿀 때 사용한다.
startInstall()라는 메서드가 있는데,
이 메서드에는 SpaceException과 MemoryException을 던진다고 선언되어있다.
예외선언은 Exception자손만 한다. (RuntimeException의 자손은 선언하지 않는 것이 보통이다. 선언해도 되긴하는데, 잘 안한다.)
SpaceException은 Exception자손이다. 즉 예외 필수처리이다. (checked예외)
그런데 이것을 예외 선택처리로 바꾸고싶다. 그러면, 상속받는 부모 Exception을 RuntimException으로 바꾸면 된다.
바꾸기만 하면 되는데, SpaceException이 이미 다른 곳에 많이 쓰이고 있다.
그러면, 이 상속 계층도를 바꾸는 것이 쉽지 않다.
그래서 SpaceException이 상속받은 Exception을 RuntimeException으로 변경하는 것이 쉽지 않을 때,
연결된 예외를 이용해서 바꾸는 것이다
RuntimeExcetpio안에다가, SpaceException을 집어넣는 것이다.
그래서 Exception이 발생한 것이아니라, RuntimeException이 발생한 것 처럼 위장하는 것이다.
코드를 보면, 이렇게 하는 것이다.
코드에서는 SpaceException이 아니라, MemoryException을 필수에서 선택처리로 바꿧는데,
원래는 위에 예외가 2개 선언되어 있었다. SpaceException, MemoryException이 있었는데,
이 코드 에서는, MemoryException이 예외처리가 필수였던 것을 예외처리 선택으로 바꾼 것이다.
그래서 thorws에 예외 처리 필수인 SpaceException만 선언해 두었고, MemoryException은 선언하지 않았다.
왜냐하면, 예외 처리가 선택으로 바뀌었기 때문이다.
바꾸는 방법은, RuntimeException을 만들고, 그 안에 원인 예외로 MemoryException을 등록하는 것이다.
아까는, initCause()라는 메서드를 사용했는데,
checked예외를 unchecked예외로 바꿀때는 RuntimeException의 생성자를 사용했다.
RuntimeException(Throwable cause)를 사용하는데, 괄호안에 원인예외를 넣어주면
이것이 RuntimeException예외의 원인예외가 되는 것이다.
두 코드를 비교해서 잘 살펴보자.
원래 SpaceException과 MemoryException이 필수 처리 예외였는데,
MemoryException을 선택 처리 예외로 바꾸고 싶다면, 위의 코드를 아래코드처럼 작성하면 된다.
RuntimeException을 만들고, 그 안에다가 MemoryException을 집어넣는 것이다.
(MemoryException을 RuntimeException의 원인예외로 등록)
왜 이렇게 필수처리 예외를 선택처리 예외로 바꾸냐면, 필수 예외를 사용하면 예외처리가 필수라서 try-catch블럭을 꼭 써야한다는 제약이 생기기 때문에, 코드 짤때 불편한 경우가 많다. 실제로는 try-catch를 안써도 되는 상황인데도 꼭 사용해야 하는 불편함이 있다는 것이다.
그러면, 코드가 너무 길고 불편해 질 것이다. 이런 이유들로 checked예외를 unchecked예외로 변경해서 사용하기도 한다.
이럴때 연결된 예외를 사용한다.
연결된 예외란,
어떤 예외를 다른 예외로 감싸는 것이고,
언제사용하냐면,
세부적인 예외들을 포괄적인 예외로 감쌀 때 사용하고,
checked예외를 unchecked예외로 변경할 때 사용한다.
'JAVA' 카테고리의 다른 글
hashCode(), toString() (0) | 2022.04.14 |
---|---|
Object클래스와 equals() (0) | 2022.04.13 |
사용자 정의 예외 만들기, 예외 던지기 (0) | 2022.04.12 |
예외 선언하기, finally블럭 (0) | 2022.04.11 |
예외 발생시키기, checked, unchecked 예외 (0) | 2022.04.11 |
댓글
이 글 공유하기
다른 글
-
hashCode(), toString()
hashCode(), toString()
2022.04.14 -
Object클래스와 equals()
Object클래스와 equals()
2022.04.13 -
사용자 정의 예외 만들기, 예외 던지기
사용자 정의 예외 만들기, 예외 던지기
2022.04.12 -
예외 선언하기, finally블럭
예외 선언하기, finally블럭
2022.04.11