如何限制该子类不能通用?

时间:2014-05-03 21:00:48

标签: java generics inheritance compiler-construction throwable

编译时错误:泛型类可能不是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中给出的原因,如下所述,并在this question中进行了解释:

  

如果泛型类是Throwable(第11.1.1节)的直接或间接子类,则是编译时错误。

     

由于Java虚拟机的catch机制仅适用于非泛型类,因此需要此限制。

问题:

  • 如何限制java.lang.Throwable的子类不是通用类?

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

3 个答案:

答案 0 :(得分: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");

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

Full source code

答案 1 :(得分:10)

  

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

这是决定将特殊情况编写到编译器本身。原因详见this question。基本上,这与 reifiable 类型有关。您可以阅读此术语here。简而言之,如果类型在编译时完全可用,则类型是可重新生成的。例如,通用类型是不可恢复的,因为它们的类型通过类型擦除来删除。出现在catch中的对象需要可再生

  

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

那里有几个选项..

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

显然,另一种选择是扩展Throwable,但这并不是一个好主意。它为您的类添加了大量功能,并为您的类的接口添加了许多新方法,您永远不会使用它们。从OOP的角度来看,您为了拥有此功能而牺牲了班级的完整性。

答案 2 :(得分:1)

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

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()向上移动层次结构。