Computer >> 컴퓨터 >  >> 프로그래밍 >> Java

Java 메서드 오버라이딩 시 throws 예외 계층 구조가 중요한 이유

자바에서 체크드 예외(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 절에 언체크드 예외를 어떻게 선언하더라도 컴파일 에러는 발생하지 않습니다. 다만 코드의 일관성과 가독성을 위해 부모 메서드의 예외 선언과 계층 구조를 맞춰 주는 것이 좋습니다.

정리

  • 오버라이딩하는 메서드는 부모 메서드와 같은 예외 또는 그 하위 타입만 던질 수 있습니다.

  • 더 넓은 상위 타입의 예외를 던지려고 하면 컴파일 에러가 발생합니다.

  • 예외를 아예 선언하지 않고 오버라이딩하는 것은 항상 허용됩니다.

  • 이 규칙은 체크드 예외에 적용되며, 언체크드 예외는 컴파일러가 검사하지 않습니다.