为啥不能在 Java 中扩展注解?

Posted

技术标签:

【中文标题】为啥不能在 Java 中扩展注解?【英文标题】:Why is it not possible to extend annotations in Java?为什么不能在 Java 中扩展注解? 【发布时间】:2010-12-10 02:13:56 【问题描述】:

我不明白为什么Java注解中没有继承,就像Java类一样。我认为这将非常有用。

例如:我想知道给定的注释是否是验证器。通过继承,我可以反射性地浏览超类以了解此注释是否扩展了ValidatorAnnotation。否则,我怎么能做到这一点?

那么,谁能给我一个做出这个设计决定的理由?

【问题讨论】:

注意,顺便说一句,所有注解都扩展java.lang.annotation.Annotation,即任何注解都是instanceof,尽管没有明确声明这一事实。 【参考方案1】:

关于它不是这样设计的原因,您可以在JSR 175 设计常见问题解答中找到答案,其中说:

为什么不支持注解子类型(一种注解类型扩展另一种)?

它使注释类型复杂化 系统,并使它更多 很难写出“具体的工具”。

“特定工具”——查询程序 已知的任意注释类型 外部程序。存根生成器, 例如,属于这一类。 这些程序将读取注释 类而不将它们加载到 虚拟机,但会加载 注释接口。

所以,是的,我想,原因只是 KISS。无论如何,似乎这个问题(以及许多其他问题)正在作为JSR 308 的一部分进行调查,您甚至可以找到具有此功能的替代编译器,该功能已由Mathias Ricken 开发。

【讨论】:

好吧,也许我很愚蠢,但我觉得很遗憾不能仅仅为了“保持简单”而扩展注释。至少,Java 设计者对类继承的想法不同:P Java 8 M7,似乎不支持子类化注解。真可惜。 @assylias JEP 104 并不是为了让子类化注释成为可能。 JEP 104 是在 Java 8 中实现的,但仍然不能对注解进行子类化(使一个注解扩展另一个注解)。【参考方案2】:

可扩展注释将有效地增加指定和维护另一个类型系统的负担。这将是一个相当独特的类型系统,因此您不能简单地应用 OO 类型范式。

在向注释引入多态性和继承时考虑所有问题(例如,当子注释更改元注释规范(如保留)时会发生什么?)

所有这些增加了哪些用例的复杂性?

您想知道给定的注释是否属于某个类别?

试试这个:

@Target(ElementType.ANNOTATION_TYPE)
public @interface Category 
    String category();


@Category(category="validator")
public @interface MyFooBarValidator 


如您所见,您可以使用提供的工具轻松地对注释进行分组和分类,而不会造成不必要的麻烦。

所以,KISS 是没有将元类型类型系统引入 Java 语言的原因。

[附注编辑]

我使用字符串只是为了演示和考虑开放式元注释。对于您自己的给定项目,您显然可以使用类别类型的枚举并为给定的注释指定多个类别(“多重继承”)。请注意,这些值完全是伪造的,仅用于演示目的:

@Target(ElementType.ANNOTATION_TYPE)
public @interface Category 
    AnnotationCategory[] category();

public enum AnnotationCategory 
    GENERAL,
    SEMANTICS,
    VALIDATION,
    ETC


@Category(category=AnnotationCategory.GENERAL, AnnotationCategory.SEMANTICS)
public @interface FooBarAnnotation 



【讨论】:

不工作将打印错误:@Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) @interface C ; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @C public @interface F class a @F public void S() @Test public void blahTest() throws NoSuchMethodException Method m = a.class.getMethod("S"); System.out.println(m.isAnnotationPresent(C.class)); 我们正在注释一个注释。 a#S有注解F。F本身被C注解了。试试看。 是的,没错。但是有没有比通过 Annotation 代理读取更干净的方法呢?【参考方案3】:

从某种意义上说,您已经有了注释 - 元注释。如果你用元信息来注解一个注解,这在很多方面相当于扩展一个额外的接口。注解是接口,所以多态性并没有真正发挥作用,而且由于它们本质上是静态的,所以不可能有运行时动态调度。

在您的验证器示例中,您可以只在注释上获取带注释的类型并查看它是否具有验证器元注释。

我能看到继承会有所帮助的唯一用例是,如果您希望能够通过超类型获取注释,但这会增加一大堆复杂性,因为给定的方法或类型可能有两个这样的注释在它上面,这意味着必须返回一个数组,而不仅仅是一个对象。

所以我认为最终的答案是用例是深奥的,并且使更多标准用例复杂化,因此不值得。

【讨论】:

【参考方案4】:

Java 注释支持的设计者进行了许多“简化”,从而损害了 Java 社区。

    没有注释子类型会使许多复杂的注释不必要地难看。不能简单地在注释中拥有一个属性,该属性可以包含三件事之一。一个人需要拥有三个独立的属性,这让开发人员感到困惑,并且需要运行时验证以确保只使用这三个属性中的一个。

    每个站点只有一个给定类型的注释。这导致了完全不必要的集合注释模式。 @Validation 和@Validations,@Image 和@Images 等。

Java 8 中正在修复第二个问题,但为时已晚。许多框架都是基于 Java 5 中的可能性编写的,现在这些 API 缺陷已经存在很长时间了。

【讨论】:

不仅如此,它还使得定义具有递归嵌套属性的属性成为不可能 - 这是由 XML 模式支持的。这使得注解严格来说不如 XML 强大【参考方案5】:

我可能迟到三年才回答这个问题,但我发现它很有趣,因为我发现自己在同一个地方。这是我的看法。您可以将注解视为枚举。它们提供一种单向的信息——使用它或丢失它。

我有一种情况,我想在网络应用程序中模拟 GET、POST、PUT 和 DELETE。我非常想要一个名为“HTTP_METHOD”的“超级”注释。后来我才明白这并不重要。好吧,我不得不在 html 表单中使用隐藏字段来识别 DELETE 和 PUT(因为 POST 和 GET 无论如何都可用)。

在服务器端,我寻找了一个名为“_method”的隐藏请求参数。如果该值为 PUT 或 DELETE,则它会覆盖关联的 HTTP 请求方法。话虽如此,我是否需要扩展注释来完成工作并不重要。所有注释看起来都一样,但在服务器端它们的处理方式不同。

所以在你的情况下,放弃扩展注释的渴望。将它们视为“标记”。它们“代表”一些信息,而不一定“操纵”一些信息。

【讨论】:

【参考方案6】:

我能想到的一件事是有多个注释的可能性。因此,您可以在同一位置添加验证器和更具体的注释。但我可能弄错了:)

【讨论】:

【参考方案7】:

从没想过,但是……看来你是对的,注解继承工具没有问题(至少我不认为它有问题)。

关于带有 'validator' 注释的示例 - 然后您可以利用 'meta-annotation' 方法。 IE。您将特定的元注释应用到整个注释界面。

【讨论】:

我可能迟了三年才回答这个问题,但我发现它很有趣,因为我发现自己在同一个地方。【参考方案8】:

我有同样的问题。不,你不能。我确实“自律”自己在注释中编写属性以遵守某些标准,因此在获得注释时,您可以在外部“嗅探”它所具有的属性是什么类型的注释。

【讨论】:

以上是关于为啥不能在 Java 中扩展注解?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 Java 8 中使用 @FunctionalInterface 注解

Annotation 注解开发介绍优势 与好处java为啥采用注解开发

ajax请求 spring mvc responsebody 注解的方法 为啥写不了cookie

为啥java类不从实现的接口继承注解?

你啥时候使用 Java 的 @Override 注解,为啥?

java注解设置字段不能为零