编译时错误:泛型类可能不是 java.lang.Throwable 的子类
public class TestGenericClass<E> extends Exception {
/*Above line will give compile error, the generic class TestGenericClass<E> may
not subclass java.lang.Throwable*/
public TestGenericClass(String msg) {
super(msg);
}
}
上述编译时错误的原因在 § jls-8.1.2中给出,如下所示,并在这个问题中进行了解释:
如果泛型类是 Throwable 的直接或间接子类(第 11.1.1 节),则会出现编译时错误。
需要此限制,因为 Java 虚拟机的 catch 机制仅适用于非泛型类。
问题:
如何限制
java.lang.Throwable
的子类不能是泛型类?或者更通用的问题是,如何限制任何类的子类不能是通用的?
如何限制java.lang.Throwable的子类不能被 泛型类?
以下是 OpenJDK 编译器执行检查的方式:
import com.sun.tools.javac.code.Symbol.*;
private void attribClassBody(Env<AttrContext> env, ClassSymbol c) {
....
// Check that a generic class doesn't extend Throwable
if (!c.type.allparams().isEmpty() && types.isSubtype(c.type, syms.throwableType))
log.error(tree.extending.pos(), "generic.throwable");
正如您所看到的,禁止类型是一种编码,因此如果没有编译器代码自定义,您就无法对自定义类使用相同的技术。
如何限制java.lang.Throwable的子类不能是泛型类?
这是一个将特殊情况写入编译器本身的决定。原因详见这个问题。基本上,这与“可具体化”的类型有关。您可以在此处阅读有关此术语的信息。简而言之,如果类型在编译时完全可用,则该类型是可具体化的。例如,泛型类型是不可具体化的,因为它们的类型是通过类型擦除来删除的。出现在catch
块中的对象
需要是可具体化的。 或者更通用的问题,如何限制类的子类不能是通用的?
嗯,有几个选择..
目前,在 Java 的正常范围内没有选项可以做到这一点。它没有某种
final
实现来阻止通用性应用于子类。正如注释中所解释的,您可以得到的最接近的东西是扩展编译器并专门为您的类添加规则。这个解决方案让我脊背发凉。躲开它。这意味着您的代码只能与
您的版本的Java一起运行,并且任何其他想要使用您的代码的人都必须安装相同的版本。 显然,另一个选择是延长
Throwable
,但这确实不是一个好主意。它为您的类添加了一大堆功能,并为您的类的接口添加了许多您永远不会使用的新方法。从 OOP 的角度来看,您为了获得此功能而牺牲了类的完整性。
如果您愿意将错误延迟到运行时,您可以在超类构造函数中使用反射来查看子类是否声明了任何类型参数。
此代码打印
Example$NonGenericSubclass@15db9742
Exception in thread "main" java.lang.AssertionError: ProhibitsGenericSubclasses prohibits generic subclasses (see docs)
at Example$ProhibitsGenericSubclasses.<init>(Example.java:12)
at Example$GenericSubclass.<init>(Example.java:17)
at Example.main(Example.java:21)
如果要禁止层次结构中所有类的类型参数,而不仅仅是最派生的类,则需要使用
Class#getSuperclass()
向上遍历层次结构。