[java] Java가 Throwable의 일반 서브 클래스를 허용하지 않는 이유는 무엇입니까?

Java Language Sepecification , 3 판 에 따르면 :

일반 클래스가의 직접 또는 간접 하위 클래스 인 경우 컴파일 타임 오류입니다 Throwable.

이 결정이 내려진 이유를 이해하고 싶습니다. 일반적인 예외의 문제점은 무엇입니까?

(내가 아는 한, 제네릭은 단순히 컴파일 타임 구문 설탕이며 파일 Object에서 어쨌든 번역 .class되므로 제네릭 클래스를 효과적으로 선언하는 것은 마치 마치 마치 모든 것이 Object. .)



답변

마크가 말했듯이 유형을 수정할 수 없으므로 다음과 같은 경우에 문제가됩니다.

try {
   doSomeStuff();
} catch (SomeException<Integer> e) {
   // ignore that
} catch (SomeException<String> e) {
   crashAndBurn()
}

모두 SomeException<Integer>SomeException<String>동일한 유형으로 지워집니다, JVM이 예외 인스턴스를 구별 할 방법과 어떤 얘기하는 것이 방법이 없습니다 catch실행되어야 블록.


답변

다음은 예외 사용 방법에 대한 간단한 예입니다.

class IntegerExceptionTest {
  public static void main(String[] args) {
    try {
      throw new IntegerException(42);
    } catch (IntegerException e) {
      assert e.getValue() == 42;
    }
  }
}

TRy 문의 본문은 주어진 값으로 예외를 처리하며 이는 catch 절에 의해 포착됩니다.

반대로, 새 예외에 대한 다음 정의는 매개 변수화 된 유형을 작성하므로 금지됩니다.

class ParametricException<T> extends Exception {  // compile-time error
  private final T value;
  public ParametricException(T value) { this.value = value; }
  public T getValue() { return value; }
}

위의 컴파일을 시도하면 오류가보고됩니다.

% javac ParametricException.java
ParametricException.java:1: a generic class may not extend
java.lang.Throwable
class ParametricException<T> extends Exception {  // compile-time error
                                     ^
1 error

이 예외는 유형을 수정할 수 없기 때문에 이러한 예외를 포착하려는 거의 모든 시도가 실패해야하기 때문에 합리적입니다. 예외의 일반적인 사용은 다음과 같습니다.

class ParametricExceptionTest {
  public static void main(String[] args) {
    try {
      throw new ParametricException<Integer>(42);
    } catch (ParametricException<Integer> e) {  // compile-time error
      assert e.getValue()==42;
    }
  }
}

catch 절의 유형을 확인할 수 없으므로 허용되지 않습니다. 이 글을 쓰는 시점에서 Sun 컴파일러는 다음과 같은 경우에 일련의 구문 오류를보고합니다.

% javac ParametricExceptionTest.java
ParametricExceptionTest.java:5: <identifier> expected
    } catch (ParametricException<Integer> e) {
                                ^
ParametricExceptionTest.java:8: ')' expected
  }
  ^
ParametricExceptionTest.java:9: '}' expected
}
 ^
3 errors

예외는 매개 변수가 될 수 없으므로 구문이 제한되어 형식이 다음 매개 변수없이 식별자로 작성되어야합니다.


답변

본질적으로 나쁜 방식으로 설계 되었기 때문입니다.

이 문제는 깨끗한 추상 디자인을 방지합니다.

public interface Repository<ID, E extends Entity<ID>> {

    E getById(ID id) throws EntityNotFoundException<E, ID>;
}

제네릭에 대한 catch 절이 실패한다는 사실은 그에 대한 변명이 아닙니다. 컴파일러는 캐치 절 내에서 Throwable을 확장하거나 제네릭을 허용하지 않는 구체적인 제네릭 형식을 허용하지 않을 수 있습니다.


답변

제네릭은 컴파일 타임에 형식이 올바른지 검사합니다. 그런 다음 일반 유형 정보는 type erasure 프로세스에서 제거됩니다 . 예를 들어, List<Integer>제네릭이 아닌 형식으로 변환됩니다 List.

유형 삭제 때문에 런타임에 유형 매개 변수를 판별 할 수 없습니다.

다음 Throwable과 같이 확장 할 수 있다고 가정 해 봅시다 .

public class GenericException<T> extends Throwable

이제 다음 코드를 고려해 봅시다.

try {
    throw new GenericException<Integer>();
}
catch(GenericException<Integer> e) {
    System.err.println("Integer");
}
catch(GenericException<String> e) {
    System.err.println("String");
}

erasure 유형 으로 인해 런타임은 실행할 catch 블록을 알 수 없습니다.

따라서 일반 클래스가 Throwable의 직접 또는 간접 서브 클래스 인 경우 컴파일 타임 오류입니다.

출처 : 유형 삭제 문제


답변

매개 변수화를 보장 할 수있는 방법이 없기 때문입니다. 다음 코드를 고려하십시오.

try
{
    doSomethingThatCanThrow();
}
catch (MyException<Foo> e)
{
    // handle it
}

아시다시피, 매개 변수화는 단지 구문 설탕입니다. 그러나 컴파일러는 컴파일 범위의 개체에 대한 모든 참조에서 매개 변수화가 일관성을 유지하도록합니다. 예외의 경우 컴파일러는 MyException이 처리중인 범위에서만 발생 함을 보장 할 방법이 없습니다.


답변