Hos*_*Aly 142 java generics language-design exception
根据Java语言规范,第3版:
我希望理解为什么做出这个决定.通用异常有什么问题?
(据我所知,泛型只是编译时的语法糖,他们将被翻译成Object反正在.class文件,因此有效地声明一个泛型类是仿佛一切都在它是一个Object,请纠正我,如果我错了.)
Tor*_*rek 149
正如mark所说,这些类型是不可恢复的,这在以下情况下是一个问题:
try {
doSomeStuff();
} catch (SomeException<Integer> e) {
// ignore that
} catch (SomeException<String> e) {
crashAndBurn()
}
Run Code Online (Sandbox Code Playgroud)
既SomeException<Integer>和SomeException<String>被擦除至相同类型的,也没有办法为JVM区分异常情况,因此没有办法知道哪些catch应该被执行的块.
IAd*_*ter 14
以下是如何使用异常的简单示例:
class IntegerExceptionTest {
public static void main(String[] args) {
try {
throw new IntegerException(42);
} catch (IntegerException e) {
assert e.getValue() == 42;
}
}
}
Run Code Online (Sandbox Code Playgroud)
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; }
}
Run Code Online (Sandbox Code Playgroud)
尝试编译上述报告错误:
% 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
Run Code Online (Sandbox Code Playgroud)
这种限制是明智的,因为几乎任何捕获此类异常的尝试都必须失败,因为该类型不可恢复.有人可能会认为异常的典型用法如下:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是不允许的,因为catch子句中的类型不可恢复.在撰写本文时,Sun编译器在这种情况下报告了一连串的语法错误:
% javac ParametricExceptionTest.java
ParametricExceptionTest.java:5: <identifier> expected
} catch (ParametricException<Integer> e) {
^
ParametricExceptionTest.java:8: ')' expected
}
^
ParametricExceptionTest.java:9: '}' expected
}
^
3 errors
Run Code Online (Sandbox Code Playgroud)
由于异常不能参数化,因此语法受到限制,因此必须将类型写为标识符,而不包含以下参数.
小智 13
这主要是因为它的设计方式很糟糕.
此问题可防止干净的抽象设计,例如
public interface Repository<ID, E extends Entity<ID>> {
E getById(ID id) throws EntityNotFoundException<E, ID>;
}
Run Code Online (Sandbox Code Playgroud)
一个catch子句因泛型而失败的事实并没有被证实是没有任何借口的.编译器可以简单地禁止在catch子句中扩展Throwable或禁止泛型的具体泛型类型.
在编译时检查泛型的类型正确性。然后在称为类型擦除的过程中删除通用类型信息。例如,List<Integer>将转换为非泛型类型List。
由于类型擦除,无法在运行时确定类型参数。
假设您可以Throwable像这样扩展:
public class GenericException<T> extends Throwable
Run Code Online (Sandbox Code Playgroud)
现在让我们考虑以下代码:
try {
throw new GenericException<Integer>();
}
catch(GenericException<Integer> e) {
System.err.println("Integer");
}
catch(GenericException<String> e) {
System.err.println("String");
}
Run Code Online (Sandbox Code Playgroud)
由于类型擦除,运行时将不知道要执行哪个 catch 块。
因此,如果泛型类是 Throwable 的直接或间接子类,则会出现编译时错误。
来源: 类型擦除问题