ChatGPT解决这个技术问题 Extra ChatGPT

为什么不能在 Java 中扩展注解?

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

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

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

请注意,顺便说一句,所有注释都扩展 java.lang.annotation.Annotation,即任何注释都是 instanceof,尽管没有明确声明这一事实。

W
Walery Strauch

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

为什么不支持注释子类型(一种注释类型扩展另一种)?它使注释类型系统复杂化,并且使编写“特定工具”变得更加困难。 …“特定工具”——查询任意外部程序的已知注释类型的程序。例如,存根生成器就属于这一类。这些程序将读取带注释的类而不将它们加载到虚拟机中,但会加载注释接口。

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


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

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

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

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

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

尝试这个:

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

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

}

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

因此,KISS 是未将元类型类型系统引入 Java 语言的原因。

[ps编辑]

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

@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 代理更清晰地做到这一点?
Y
Yishai

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

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

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

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


K
Konstantin Komissarchik

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

没有注释子类型会使许多复杂的注释不必要地难看。不能简单地在注释中拥有一个可以包含三件事之一的属性。一个需要具有三个单独的属性,这会使开发人员感到困惑,并且需要运行时验证以确保仅使用这三个属性中的一个。每个站点只有一个给定类型的注释。这导致了完全不必要的集合注释模式。 @Validation 和@Validations,@Image 和@Images 等。

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


不仅如此,它还使得定义具有递归嵌套属性的属性成为不可能——这由 XML 模式支持。这使得注解严格来说不如 XML 强大
N
Necreaux

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

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

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

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


J
Janusz

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


d
denis.zhdanov

从来没有想过,但是......似乎你是对的,注释继承工具没有问题(至少我没有看到它的问题)。

关于带有“验证器”注释的示例 - 然后您可以利用“元注释”方法。即,您将特定的元注释应用于整个注释界面。


我可能迟了三年才回答这个问题,但我发现它很有趣,因为我发现自己在同一个地方。
a
ante.sabo

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