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

时间:2016-05-06 04:39:02

标签: java annotations retention

我非常了解RetentionPolicy的含义,知道他们做了什么,何时做seems to make sense to use them。对于我自己的注释,我确切地知道它们是否需要在运行时,类文件中,或仅用于编译。但是,对于库中定义的任何注释,您可以恕我直言。

例如,javax.annotation.Generated用于标记生成的代码,但它很少有用。由于AFAIK有更多的工具在字节码上工作而不是使用源的工具,因此信息在使用之前就消失了。

由于注释在运行时don't throw ClassNotFoundException不存在(与例如缺少接口不同),使用RetentionPolicy.RUNTIME似乎不会造成任何伤害。或者我错了吗?

或者保存几个字节是使用不同Retention s的原因?对我来说,这似乎导致太多问题值得。我错过了什么?

4 个答案:

答案 0 :(得分:14)

Java Annotations的灵感发生在2002年之前,围绕从Java 1.3到Java 1.4的过渡。当时的高规格桌面是大约2.5GHz的Pentium 4或大约2GHz的Athlon XP +,RAM为256或512MB。例如,评论here

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

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

的介绍
  

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

他们解决问题的方法是将问题分成三个用例:

  

<强> VI。阅读注释

     

注释消费者可分为三组:

     

一个。 “Introspectors” - 查询运行时可见注释的程序   他们自己的程序元素。这些程序将加载两个注释   类和注释接口到虚拟机中。 (通过   运行时可见,我们指的是保留的注释   政策是RUNTIME。)

     

湾“特定工具” - 查询已知的程序   任意外部程序的注释类型。 Stub发电机,用于   例如,属于这一类。这些程序将读取注释   类没有将它们加载到虚拟机中,但会加载   注释接口。

     

℃。 “常规工具” - 查询的程序   任意外部程序的任意注释(如   编译器,文档生成器和类浏览器)。这些   程序将既不加载带注释的类也不加载注释接口   进入虚拟机。据说这些节目是“手挽手”   长度“。

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

所以,是的,保留策略已经到位,以节省字节和运行时开销。虽然现在看起来很古怪,但2002年是一个不同的世界,记忆和表现是非常真实的问题。现在我们拥有10倍的性能和内存,您可以安全地使用RUNTIME保留而无需担心。

答案 1 :(得分:3)

  

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

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

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

示例:

@SuppressWarnings让你压制警告,你怎么能这样做? C#允许您定义#PRAGMA#IF,某种条件编译。条件编译信息都不存储在编译输出中。

@Override允许Java编译器检查基类是否有覆盖或不覆盖的方法,如果使用错误的参数定义新方法,java编译器将使用带有重载的新方法编译类,但是存在@Override java编译器会给你一个错误,签名不正确以覆盖方法。

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

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

以下是

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

结论

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

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

答案 2 :(得分:1)

注释的主要目的是为编译单元提供元数据。大多数标准注释都清楚地表达了有助于代码开发和编译的元信息(通过指定可由IDE或编译器验证的属性)。

注释不是为修改语言的运行时语义而设计的。因此,注释在运行时是否可用并不会改变执行。 (当然,如果你激活使用元信息来调整你的实现行为,那么一切皆有可能。)

如果在库jar中,某个注释被标记为RetentionPolicy.RUNTIME,显然需要从运行时访问注释(使用反射)对以后的用户有用。
如果同时注释的实现来自另一个库,那么这种期望要么没有保证,要么是由于这个注释的特定目的可能仅对某些用例有用。 (并且仅针对不同的保留设置构建不同的jar版本是不合适的。)

因此,如果开发人员将注释标记为RetentionPolicy.RUNTIME,则需要考虑运行时访问的明确用例。然后,用相同的jar或不同的jar提供注释实现可以独立于用例(例如,基于其他结构化标准)。在任何情况下,如果您打算从此用例中受益,您将在类路径中拥有此注释库(因为您可能还需要其他组件)并且一切正常。如果您不适用于此用例,那么您将不会受到缺失的注释实现的影响。

回答你的问题:

使用RUNTIME保留对程序没有任何损害,除了带有死信息的混乱(字节码)可执行文件。仅在预期(并且认为有用)运行时使用元信息的情况下使用RUNTIME保留会增加代码质量(在可维护性和可理解性方面)。

答案 3 :(得分:1)

@Retention

此批注指定标记的批注如何存储在Java运行时中。它是仅限于源代码,嵌入到生成的类文件中,还是在运行时通过反射也可用。 因此指示要保留带注释类型的注释的时间长度。如果注释类型声明中不存在保留注释,则保留策略默认为RetentionPolicy.CLASS。

保留策略确定应丢弃注释的位置。 Java通过java.lang.annotation.RetentionPolicy枚举定义了3种类型的保留策略。它有SOURCE,CLASS和RUNTIME。 具有保留策略的注释SOURCE将仅与源代码一起保留,并在编译期间被丢弃。 保留策略CLASS的注释将保留,直到编译代码,并在运行时丢弃。 具有保留策略的注释RUNTIME将通过运行时提供给JVM。 保留策略将使用java内置注释@Retention指定,我们必须传递保留策略类型。

默认保留策略类型为CLASS。