18

编译时错误:泛型类可能不是 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不是泛型类?

  • 或者更通用的问题是,如何限制任何类的子类不能是通用的?

4

3 回答 3

20

如何限制 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");

正如您所看到的,禁止类型是一种硬编码,因此如果没有编译器代码自定义,您就不能对自定义类使用相同的技术。

完整的源代码

于 2014-05-03T21:30:04.943 回答
10

如何限制 java.lang.Throwable 的子类不是泛型类?

这是将特殊情况写入编译器本身的决定。这个问题中详细说明了原因。基本上,这与类型是reifiable 有关您可以在此处阅读有关此术语的信息。简而言之,如果类型在编译时完全可用,则该类型是可具体化的。例如,泛型类型是不可具体化的,因为它们的类型被类型擦除删除了。出现在catch块中的对象需要是可具体化的

或者更通用的问题,如何限制一个类的子类不能是通用的?

好吧有几个选择..

目前,在 Java 的正常范围内没有选项可以做到这一点。它没有某种final阻止将泛型应用于子类的实现。正如评论中所解释的,最接近这一点的方法是扩展编译器并专门为您的类添加规则。这个解决方案让我脊背发凉。躲开它。这意味着您的代码只会与的 Java 版本一起运行,并且任何想要使用您的代码的人都必须安装相同的版本。

显然,另一种选择是扩展Throwable,但这确实不是一个好主意。它为您的类添加了一大堆功能,并为您的类的接口添加了许多您永远不会使用的新方法。从 OOP 的角度来看,您牺牲了类的完整性来换取这个特性。

于 2014-05-03T21:22:41.903 回答
1

如果您愿意将错误延迟到运行时,您可以在超类构造函数中使用反射来查看子类是否声明了任何类型参数。

public class Example {
    public static class ProhibitsGenericSubclasses {
        public ProhibitsGenericSubclasses() {
            if (getClass().getTypeParameters().length > 0)
                throw new AssertionError("ProhibitsGenericSubclasses prohibits generic subclasses (see docs)");
        }
    }

    public static class NonGenericSubclass extends ProhibitsGenericSubclasses {}
    public static class GenericSubclass<T> extends ProhibitsGenericSubclasses {}

    public static void main(String[] args) {
        System.out.println(new NonGenericSubclass());
        System.out.println(new GenericSubclass<Object>());
    }
}

此代码打印

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().

于 2014-05-08T06:01:05.670 回答