为什么 Java 不允许 Throwable 的泛型子类?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/501277/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why doesn't Java allow generic subclasses of Throwable?
提问by Hosam Aly
According to the Java Language Sepecification, 3rd edition:
根据Java 语言规范,第 3 版:
It is a compile-time error if a generic class is a direct or indirect subclass of
Throwable
.
I wish to understand why this decision has been made. What's wrong with generic exceptions?
我想了解为什么做出这个决定。通用异常有什么问题?
(As far as I know, generics are simply compile-time syntactic sugar, and they will be translated to Object
anyway in the .class
files, so effectively declaring a generic class is as if everything in it was an Object
. Please correct me if I'm wrong.)
(据我所知,泛型只是编译时的语法糖,它们将Object
在.class
文件中以任何方式转换,因此有效地声明泛型类就好像其中的所有内容都是Object
. 如果我错了,请纠正我.)
采纳答案by Torsten Marek
As mark said, the types are not reifiable, which is a problem in the following case:
正如马克所说,类型是不可具体化的,这在以下情况下是一个问题:
try {
doSomeStuff();
} catch (SomeException<Integer> e) {
// ignore that
} catch (SomeException<String> e) {
crashAndBurn()
}
Both SomeException<Integer>
and SomeException<String>
are erased to the same type, there is no way for the JVM to distinguish the exception instances, and therefore no way to tell which catch
block should be executed.
既SomeException<Integer>
和SomeException<String>
被擦除至相同类型的,也没有办法为JVM区分异常情况,因此没有办法知道哪些catch
应该被执行的块。
回答by IAdapter
Here is a simple example of how to use the exception:
下面是一个如何使用异常的简单示例:
class IntegerExceptionTest {
public static void main(String[] args) {
try {
throw new IntegerException(42);
} catch (IntegerException e) {
assert e.getValue() == 42;
}
}
}
The body of the TRy statement throws the exception with a given value, which is caught by the catch clause.
TRy 语句的主体抛出具有给定值的异常,该值由 catch 子句捕获。
In contrast, the following definition of a new exception is prohibited, because it creates a parameterized type:
相反,禁止对新异常进行以下定义,因为它创建了参数化类型:
class ParametricException<T> extends Exception { // compile-time error
private final T value;
public ParametricException(T value) { this.value = value; }
public T getValue() { return value; }
}
An attempt to compile the above reports an error:
尝试编译上面的报错:
% 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
This restriction is sensible because almost any attempt to catch such an exception must fail, because the type is not reifiable. One might expect a typical use of the exception to be something like the following:
这种限制是明智的,因为几乎任何捕获此类异常的尝试都必须失败,因为该类型不可具体化。人们可能期望异常的典型用法如下所示:
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;
}
}
}
This is not permitted, because the type in the catch clause is not reifiable. At the time of this writing, the Sun compiler reports a cascade of syntax errors in such a case:
这是不允许的,因为 catch 子句中的类型不可具体化。在撰写本文时,Sun 编译器会在这种情况下报告一系列语法错误:
% javac ParametricExceptionTest.java
ParametricExceptionTest.java:5: <identifier> expected
} catch (ParametricException<Integer> e) {
^
ParametricExceptionTest.java:8: ')' expected
}
^
ParametricExceptionTest.java:9: '}' expected
}
^
3 errors
Because exceptions cannot be parametric, the syntax is restricted so that the type must be written as an identifier, with no following parameter.
由于异常不能参数化,因此语法受到限制,因此必须将类型写为标识符,并且没有后面的参数。
回答by kdgregory
I would expect that it's because there's no way to guarantee the parameterization. Consider the following code:
我希望这是因为无法保证参数化。考虑以下代码:
try
{
doSomethingThatCanThrow();
}
catch (MyException<Foo> e)
{
// handle it
}
As you note, parameterization is just syntactic sugar. However, the compiler tries to ensure that parameterization remains consistent across all references to an object in compilation scope. In the case of an exception, the compiler has no way to guarantee that MyException is only thrown from a scope that it is processing.
正如您所注意到的,参数化只是语法糖。但是,编译器会尝试确保参数化在编译范围内对对象的所有引用之间保持一致。在出现异常的情况下,编译器无法保证 MyException 仅从它正在处理的范围内抛出。
回答by Michele Sollecito
It's essentially because it was designed in a bad way.
这主要是因为它的设计方式很糟糕。
This issue prevents clean abstract design e.g.,
这个问题阻止了干净的抽象设计,例如,
public interface Repository<ID, E extends Entity<ID>> {
E getById(ID id) throws EntityNotFoundException<E, ID>;
}
The fact that a catch clause would fail for generics are not reified is no excuse for that. The compiler could simply disallow concrete generic types that extend Throwable or disallow generics inside catch clauses.
对于泛型没有具体化,catch 子句会失败的事实并不是这样做的借口。编译器可以简单地禁止扩展 Throwable 的具体泛型类型或禁止在 catch 子句中使用泛型。
回答by outdev
Generics are checked at compile-time for type-correctness. The generic type information is then removed in a process called type erasure. For example, List<Integer>
will be converted to the non-generic type List
.
在编译时检查泛型的类型正确性。然后在称为类型擦除的过程中删除通用类型信息。例如,List<Integer>
将转换为非泛型类型List
。
Because of type erasure, type parameters cannot be determined at run-time.
由于类型擦除,无法在运行时确定类型参数。
Let's assume you are allowed to extend Throwable
like this:
假设您可以Throwable
像这样扩展:
public class GenericException<T> extends Throwable
Now let's consider the following code:
现在让我们考虑以下代码:
try {
throw new GenericException<Integer>();
}
catch(GenericException<Integer> e) {
System.err.println("Integer");
}
catch(GenericException<String> e) {
System.err.println("String");
}
Due to type erasure, the runtime will not know which catch block to execute.
由于类型擦除,运行时将不知道要执行哪个 catch 块。
Therefore it is a compile-time error if a generic class is a direct or indirect subclass of Throwable.
因此,如果泛型类是 Throwable 的直接或间接子类,则会出现编译时错误。
Source:Problems with type erasure
来源:类型擦除问题