Java 如何限制子类不能是泛型的?

Java 如何限制子类不能是泛型的?,java,generics,inheritance,compiler-construction,throwable,Java,Generics,Inheritance,Compiler Construction,Throwable,编译时错误:泛型类不能子类java.lang.Throwable 公共类TestGenericClass扩展异常{ /*上面的行将给出编译错误,泛型类TestGenericClass可能会 不是子类java.lang.Throwable*/ 公共TestGenericClass(字符串消息){ 超级(味精); } } 上述编译时错误的原因如下所示,并在中解释: 如果泛型类是Throwable(§11.1.1)的直接或间接子类,则为编译时错误 由于Java虚拟机的捕获机制仅适用于非泛型类,因此需

编译时错误:泛型类不能子类java.lang.Throwable

公共类TestGenericClass扩展异常{
/*上面的行将给出编译错误,泛型类TestGenericClass可能会
不是子类java.lang.Throwable*/
公共TestGenericClass(字符串消息){
超级(味精);
}
}
上述编译时错误的原因如下所示,并在中解释:

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

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

问题:

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

  • 或者更一般的问题是,如何限制任何类的子类不能是泛型的

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

这是一个将特殊情况写入编译器本身的决定。原因详见。基本上,这与类型是可恢复的有关。你可以阅读关于这个学期的内容。简而言之,如果一个类型在编译时完全可用,那么它是可修改的。例如,泛型类型是不可重新定义的,因为它们的类型是通过类型擦除删除的。出现在
catch
块中的对象需要可重新定义

或者更一般的问题,如何限制类的子类不能是泛型的

嗯,有几个选择。

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

显然,另一个选项是扩展可丢弃的
,但这确实不是一个好主意。它向类中添加了大量功能,并向类的接口中添加了许多新方法,这些方法是您永远不会使用的。从面向对象的角度来看,为了拥有这个特性,您牺牲了类的完整性

如何限制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");
import com.sun.tools.javac.code.Symbol.*;

private void attribClassBody(Env

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

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>());
    }
}
公共类示例{
公共静态类ProhibitGenericSubclass{
公共ProhibitsGenericSubclasses(){
如果(getClass().getTypeParameters().length>0)
抛出新的断言错误(“ProhibitGenericSubclasses禁止泛型子类(参见文档)”;
}
}
公共静态类NonGenericSubclass扩展了prohibitsgenericsubclass{}
公共静态类GenericSubclass扩展了ProhibitGenericSubclass{}
公共静态void main(字符串[]args){
System.out.println(新的NonGenericSubclass());
System.out.println(新的GenericSubclass());
}
}
此代码打印

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)
示例$NonGenericSubclass@15db9742
线程“main”java.lang.AssertionError中的异常:ProHibitGenericSubclass禁止泛型子类(请参阅文档)
例如$ProhibitsGenericSubclasses。(Example.java:12)
示例$GenericSubclass。(Example.java:17)
main(Example.java:21)

如果要禁止层次结构中所有类的类型参数,而不仅仅是最派生的类,则需要使用
class\getSuperclass()在层次结构中遍历

我不完全确定他们是否使用实际的Java工具。这可能只是他们在编译器中包含的一种特殊情况。如果是这种情况,那么我如何创建自己的自定义类,而该类将没有
泛型
类。您不能。这是在编译器中完成的。您可以随时扩展编译器并为其添加规则你的类。只要让你的类扩展可丢弃的
,如果你必须让编译器在有人试图这样做时抱怨。@Bhesh,我想可能有人想扔它…@boristeshider哈哈,对不起。我知道你建议了这个解决方案。我一看到“扩展编译器”,我真的有点畏缩。显然没有冒犯的意思!这并不是一个严肃的解决方案。编写编译器应该留给编译器编写者。注释预处理器相当简单。或者甚至像Checkstyle规则。这些当然是有效的选项!您能添加类名和/或任何其他源代码吗s?虽然有黑客行为,但可以通过使基类扩展为可丢弃的来防止子类成为泛型:)如果试图在标准JVM上运行在跳过此检查的编译器上编写的代码,会发生什么情况?@hexafrance我猜没有什么不同。该限制仅用于
catch
不必在编译时支持泛型。(无论如何,任何与泛型相关的东西都只能在Java编译时使用。)@Hexafrance我所知道的“标准JVM”的唯一规范是Oracle的Java虚拟机规范。这个规范没有涵盖我们正在讨论的用例,所以我认为对于“标准JVM”,行为是未定义的。
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)