자바에서 체크드 예외(checked exception)를 처리하려면, 발생한 예외 클래스 자신 또는 그 상위 클래스를 사용해 catch 블록으로 잡아야 합니다. 메서드 오버라이딩(재정의) 시에도 예외 선언(throws)에 관해 비슷한 규칙이 적용됩니다.
슈퍼 클래스의 메서드를 오버라이딩할 때 지켜야 할 예외 관련 규칙은 다음과 같습니다.
서브 클래스의 메서드는 부모 메서드가 던지는 예외와 동일한 예외 또는 그 하위 타입(subtype)을 던져야 합니다.
서브 클래스의 메서드가 부모 메서드보다 상위 타입(supertype)의 예외를 던져서는 안 됩니다.
예외를 전혀 선언하지 않고 오버라이딩하는 것도 가능합니다.
규칙을 지키는 경우: 더 좁은 예외로 오버라이딩
Demo, SuperTest, Test 세 클래스가 상속 계층 구조를 이루고 있고, 각각 sample() 메서드를 가지고 있다고 가정해 보겠습니다. 부모 클래스인 Demo의 sample()은 IOException을 던지고, 이를 상속받은 SuperTest의 sample()은 IOException의 하위 클래스인 EOFException을 던집니다.
import java.io.IOException;
import java.io.EOFException;
class Demo {
public void sample() throws IOException {
System.out.println("sample() 메서드 - Demo 클래스");
}
}
class SuperTest extends Demo {
public void sample() throws EOFException {
System.out.println("sample() 메서드 - SuperTest 클래스");
}
}
public class Test extends SuperTest {
public static void main(String args[]) {
Demo obj = new SuperTest();
try {
obj.sample();
} catch (EOFException ex) {
System.out.println("예외 발생");
}
}
}
실행 결과
sample() 메서드 - SuperTest 클래스
EOFException은 IOException보다 범위가 좁은, 즉 더 구체적인 예외이므로 오버라이딩 규칙을 위반하지 않습니다. 따라서 이 코드는 문제없이 컴파일되고 실행됩니다.
규칙을 어기는 경우: 더 넓은 예외로 오버라이딩
이번에는 반대로 부모 클래스가 좁은 예외를 던지고, 자식 클래스가 더 넓은 상위 타입의 예외를 던지도록 바꿔 보겠습니다.
import java.io.IOException;
import java.io.EOFException;
class Demo {
public void sample() throws EOFException {
System.out.println("sample() 메서드 - Demo 클래스");
}
}
class SuperTest extends Demo {
public void sample() throws IOException {
System.out.println("sample() 메서드 - SuperTest 클래스");
}
}
public class Test extends SuperTest {
public static void main(String args[]) {
Demo obj = new SuperTest();
try {
obj.sample();
} catch (IOException ex) {
System.out.println("예외 발생");
}
}
}
컴파일 결과
Test.java:11: error: sample() in SuperTest cannot override sample() in Demo
public void sample() throws IOException {
^
overridden method does not throw IOException
1 error
부모 메서드는 EOFException만 던진다고 선언했는데, 자식 메서드가 더 넓은 범위인 IOException을 던지려고 했기 때문입니다. 이렇게 허용된다면 다형성(polymorphism)으로 부모 타입 참조 변수를 통해 메서드를 호출하는 호출자 입장에서, 선언되지 않은 예외를 갑자기 처리해야 하는 상황이 생길 수 있습니다. 자바 컴파일러는 이를 허용하지 않으며 컴파일 타임 에러를 발생시킵니다.
언체크드 예외는 어떨까?
RuntimeException 및 Error 계열의 언체크드 예외(unchecked exception)는 컴파일러가 검사 대상에 포함하지 않습니다. 따라서 오버라이딩 시 throws 절에 언체크드 예외를 어떻게 선언하더라도 컴파일 에러는 발생하지 않습니다. 다만 코드의 일관성과 가독성을 위해 부모 메서드의 예외 선언과 계층 구조를 맞춰 주는 것이 좋습니다.
정리
오버라이딩하는 메서드는 부모 메서드와 같은 예외 또는 그 하위 타입만 던질 수 있습니다.
더 넓은 상위 타입의 예외를 던지려고 하면 컴파일 에러가 발생합니다.
예외를 아예 선언하지 않고 오버라이딩하는 것은 항상 허용됩니다.
이 규칙은 체크드 예외에 적용되며, 언체크드 예외는 컴파일러가 검사하지 않습니다.