我非常了解RetentionPolicy
的含义,知道他们做了什么,何时做seems to make sense to use them。对于我自己的注释,我确切地知道它们是否需要在运行时,类文件中,或仅用于编译。但是,对于库中定义的任何注释,您可以恕我直言。
例如,javax.annotation.Generated
用于标记生成的代码,但它很少有用。由于AFAIK有更多的工具在字节码上工作而不是使用源的工具,因此信息在使用之前就消失了。
由于注释在运行时don't throw ClassNotFoundException
不存在(与例如缺少接口不同),使用RetentionPolicy.RUNTIME
似乎不会造成任何伤害。或者我错了吗?
或者保存几个字节是使用不同Retention
s的原因?对我来说,这似乎导致太多问题值得。我错过了什么?
答案 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指定,我们必须传递保留策略类型。