자바의 예외 규칙
자바에서 부모 클래스를 상속받거나, 인터페이스를 구현할 때 다음과 같은 예외 관련 규칙이 있다.
- 체크 예외
- 부모 메서드가 체크 예외를 던지지 않으면, 재정의된 자식 메서드도 체크 예외를 던질 수 없다.
- 자식 메서드는 부모 메서드가 던질 수 있는 체크 예외의 하위 타입만 던질 수 있다.
- 언체크(런타임) 예외
- 예외 처리를 강제하지 않으므로 상관없이 던질 수 있다.
예외 규칙의 이유
런타임 예외는 제약이 없지만, 체크 예외는 위와 같은 제약이 있다. 이런 제약을 두는 이유는 무엇일까? 위와 같은 제약이 없다면 어떻게 될까?
다음 예제는 자식 클래스에서 부모 클래스가 던진 예외보다 상위 예외를 던지는 경우이다. (예제 코드일 뿐, 실제로는 컴파일 오류 발생)
class Parent {
void method() throws InterruptedException {
// ...
}
}
class Child extends Parent {
@Override
void method() throws Exception {
// ...
}
}
public class Test {
public static void main(String[] args) {
Parent p = new Child();
try {
p.method(); // 컴파일러는 Parent의 메서드를 호출한 것으로 인지
} catch (InterruptedException e) {
// InterruptedException 처리
}
}
}
Test 코드에서 컴파일러는 Parent p의 메서드를 호출한 것으로 인지한다. 따라서 try catch문에서 부모 클래스가 던진 예외인 InterruptedException을 잡아 처리한다. 그런데 런타임 시점에 자식 클래스의 메서드에서 더 상위 예외인 Exception을 던진다면? 체크 예외는 명시적으로 throws하거나 잡아서 처리해야 하는데 Test 코드에서 체크 예외를 명시적으로 잡아 처리하지 못하게 된다. 즉, 확실하게 모든 예외를 체크해야 하는 체크 예외의 규약에 맞지 않게 된다.
Runnable 인터페이스
Runnable 인터페이스를 보면 run() 메서드에서 아무 예외를 던지지 않는다.
@FunctionalInterface
public interface Runnable {
/**
* Runs this operation.
*/
void run();
}
따라서 Runnable을 구현한 (스레드) 클래스에서는 어떠한 예외도 throws할 수 없다. 따라서 다음과 같이 클래스에서 예외가 발생할 경우, 반드시 try catch로 잡아줘야 한다.
public class MyThread implements Runnable {
@Override
public void run() { // 예외를 throw 불가
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
이를 통해 해당 스레드에서 발생하는 예외는 해당 스레드가 종료되기 전에 반드시 처리하게 된다. 이를 통해 스레드의 안정성과 일관성을 유지할 수 있다.
Reference
- 김영한의 실전 자바 - 고급 1편, 멀티스레드와 동시성
'Language > Java' 카테고리의 다른 글
Executor 프레임워크(ExecutorService, Callable, Future) (0) | 2024.10.18 |
---|---|
인터럽트 (0) | 2024.10.14 |
스레드의 생명주기 (0) | 2024.10.14 |
스레드 로컬(ThreadLocal) (0) | 2024.07.08 |
Comparable과 Comparator (1) | 2024.01.15 |