java.lang.annotation.Retention的原因是什么?

2022-09-02 13:35:30

我很清楚的含义,并知道它们的作用以及何时使用它们似乎有意义。对于我自己的注释,我确切地知道它们是在运行时,在类文件中还是仅在编译时需要。但是,对于库中定义的任何注释,恕我直言,您永远无法确定。RetentionPolicy

例如,用于标记生成的代码,但它很少有用。由于AFAIK处理字节码的工具比处理源的工具多,因此信息在可以使用之前就消失了。javax.annotation.Generated

由于运行时缺少的注释不会抛出(与例如缺少接口不同),因此使用似乎没有造成任何伤害。还是我错了?ClassNotFoundExceptionRetentionPolicy.RUNTIME

还是节省几个字节是使用不同s的原因?对我来说,这似乎造成了太多的问题,不值得。我错过了什么?Retention


答案 1

Java Annotations的灵感来自2002年之前,大约是从Java 1.3到Java 1.4的过渡。当时的高规格台式机是大约2.5GHz的Pentium 4或大约2GHz的Athlon XP +,RAM将是256或512MB。例如,在这里进行评论。

问题是如何存储和检索有关代码的元数据。典型的解决方案是使用未经类型检查或直接链接到源代码的 XML 文件。其他人已经在非正式地扩展JavaDoc(源代码和扩展API存在于JDK中)用于代码生成工具。解决方案,注释,是黑客(一个非常好的黑客),它扩展了Javadoc和JLS类规范。

很明显,原作者担心性能(在2002年,Java仍然相对较慢,反射非常慢,Java运行时是一个巨大的内存消耗;有些事情永远不会改变)。这是从JSR-175的介绍:

由于许多注释仅由开发工具(如存根生成器)使用,因此在运行时保留所有注释没有多大意义。这样做可能会增加运行时内存占用量并损害性能。但是,有些批注类型在运行时很有用,有些在只能访问类文件(而不是源文件)的工具中很有用。因此,编译器将某些注释存储在类文件属性 (JVMS 4.7) 中,然后通过新的反射 API 在运行时可以使用其中一些注释进行检查。

他们对这个问题的解决方案是将问题分为三个用例:

六、阅读注释

注释使用者可分为三组:

a. “内省函数” - 查询其自身程序元素的运行时可见注释的程序。这些程序会将带注释的类和注释接口加载到虚拟机中。(通过运行时可见,我们指的是保留策略为 RUNTIME 的注释。

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

c. “通用工具” - 查询任意外部程序(如编译器、文档生成器和类浏览器)的任意注释的程序。这些程序既不会将带注释的类,也不会将注释接口加载到虚拟机中。据说这些程序“以一定的距离”运作。

这允许(当时)上面定义的“特定工具”和“通用工具”的重要用例在不给运行时带来负担的情况下完成它们的事情;对于这些工具,注释可以是 SOURCE 或 CLASS。只有运行时需要的注释(从上面看,很明显这被认为是少数用例)才会被加载并保留在JVM中。

因此,是的,保留了策略是为了节省字节和运行时开销。虽然现在看起来很古怪,但2002年是一个不同的世界,内存和性能是非常现实的问题。现在,我们的性能和内存达到了 10 倍,您可以放心地使用 RUNTIME 保留期。


答案 2

例如,javax.annotation.Generate 旨在标记生成的代码,但它很少有用。由于AFAIK处理字节码的工具比处理源的工具多,因此信息在可以使用之前就消失了。

看看源代码编辑器,从JetBrains派生的Android Studio和许多其他IDE,需要处理源代码,它提供了所有出色的编辑体验,只是因为编译时注释。

在编辑类(尚未编译)时,编辑器可以存储和处理批注。

例:

@SuppressWarnings让我们抑制警告,否则你怎么能这样做?C# 允许您定义 、 、 某种条件编译。任何条件编译信息都不会存储在已编译的输出中。#PRAGMA#IF

@Override允许Java编译器检查基类是否有要重写的方法,如果您使用错误的参数定义新方法,Java编译器将使用具有重载的新方法编译类,但是在存在java编译器的情况下,会给您一个错误,即签名不匹配以覆盖该方法。@Override

@GeneratedCode允许 IDE 跳过在使用“查找和替换”进行搜索时要显示的类和成员,并且它允许您仅对代码而不是生成的代码操作 IDE。你有没有看过Android中的资源,这些生成的类隐藏在Android Studio中,但它们确实提供了有用的代码完成列表。R.*

同样,许多这样的注释允许您进行代码分析,编写单元测试等,并在编译之前对其进行更多工作。

这里还有更多

许多 ORM 框架使用编译时注释,并生成用于类型化查询和其他帮助器类的有用额外类,以创建表和维护架构。

结论

在上面显示的示例中,很明显,所有三个和许多这样的注释都将不必要地添加如此多的字节,这些字节在运行时完全无用。

Java有两个选项,一个是使用基于c的语言中使用的etc指令添加某种编译时注释。这将需要新的语法和新的编辑经验等,另一个是创建。在不破坏语法的情况下进行创建是一个很好的举动。#IFRetentionRetention


推荐