进阶指南!深入理解 Java 注解
添加微信:code_7steps,备注“进群”,邀请你加入大牛云集的技术交流群!
你可以在 Java 开发过程中,遇到过需要将元数据(描述其他数据的数据)与类、方法或者其他元素相关联的情况。
例如,在开发过程中,需要识别大型应用程序中未完成的类。对于每个未完成的类,元数据可能包含未完成类的开发人员的项目、预计完成时间。
在 Java 5 之前,注释(Comments)是 Java 提供将元数据与应用程序元素相关联的唯一灵活机制。
但是,注释是一种非常非常不好的选择。
由于编译器会忽略它们,incident,注释在运行过程中是没有意义的。即使它们可用,也必须对文本进行解析,以获得关键的数据项。
但是,这对注释的规范性有严格的要求。
Java 5 通过引入注解(annotations),将元数据与各种应用程序元素相关联起来,这种标准机制来改变了一切。
该机制包括四个部分:
用于声明注解类型的
@interface
机制元注释类型,可用于标识注释类型适用的应用程序元素, 标识注解的生命周期(注释类型的实例)
通过对 Java Reflection API 的扩展来支持注解处理
标准注解类型
在本文中,我将向大家解释如何使用这些组件。
使用 @interface 声明注解类型
你可以通过@
符号,加上interface
这个保留字符来声明注解类型。
例如,下面示例,声明了一个简单的注解类型,你可以使用它用它来注解ThreadSafe
代码:
声明此注解类型之后,通过在此类型的实例前面加上@
加上类型名称进行注解,在你认为线程安全的方法前面加上这种类型的实例。
下面示例中,其中main()
方法带有@ThreadSafe
注释:
ThreadSafe
实例除了注解类型名称外不提供其他元数据。
但是,你可以通过向此类型添加元素来提供元数据,其中元素是放置在注释类型的主体中的方法标题。
除了没有代码主体之外,元素还受到以下限制:
方法标题不能声明参数
方法标题不能提供 throws 子句
方法标头的返回类型必须是原始类型(例如 int),java.lang.String,java.lang.Class,枚举,注释类型或这些类型之一的数组。不能为返回类型指定其他类型。作为另一个示例,清单 3 给出了一个 ToDo 注释类型,其中包含三个元素,这些元素标识特定的编码作业,指定要完成该作业的日期,并命名负责完成该作业的编码器。
下面来看一下另外一个示例,给出了一个 ToDo 注释类型,其中包含三个元素,这些元素标识特定的编码任务,指定要完成该任务的日期,并命名负责完成该任务的人员。
请注意,每个元素都没有声明任何参数或 throws 子句,具有符合规范的返回类型(int 或 String),并以分号结尾。另外,最后一个元素显示可以指定默认返回值;如果注释未为元素分配值,则返回此值。
用ToDo
来注解类方法:
上述示例中,为每个参数分配了一个元数据项。例如,将 1000 分配给 id。与 coder 不同,必须指定 id 和 finishDate 元素。否则,编译器将报告错误。未为编码器分配值时,它将采用默认值“ n / a”。
使用元注解(meta-annotation)
你可以注解多种类型,例如,类,方法,局部变量等。
但是,这种灵活性可能会带来问题。例如,可能只想将ToDo
限制为方法,但是没有什么可以阻止ToDo
用于注释其他应用类型元素的机制:
来看一个示例,
在清单 7 中,ToDo 既用于注解 AnnDemo 类,又要勇于注解局部变量cities
。
这些错误的注解可能会使阅读你代码的人员感到困惑。
在需要缩小注释类型的灵活性的时代,Java 在其 java.lang.annotation 包中提供了Target
注释类型。
Target
是元注解类型,它注释注释了注释类型,其注释那些注释了应用程序元素,例如类和方法。
它标识注释类型适用的应用程序元素的类型。
这些元素由Target
的ElementValue[]
, value[]
元素标识。
java.lang.annotation.ElementType
是一个枚举,其常量描述应用程序元素。
例如,CONSTRUCTOR 适用于构造函数,而 PARAMETER 适用于参数。
下面实例中的 ToDo 注解中,将其限制为仅注解方法:
其他元注解类型
Java 5 引入了三种元注解类型,可以在 java.lang.annotation 包中找到它们:
Retention
指示带有注释类型的注释要保留多长时间。Documented
表示由 Javadoc 和类似工具记录Documented
注解的实例。Inherited
表示注解类型是自动继承的。
Java 8 引入了 java.lang.annotation.Repeatable 元注解类型。Repeatable 用于指示注解类型是可重复的。
换句话说,你可以将来自同一可重复注释类型的多个注释应用于一个 application 元素,如下所示:
通过对 Java Reflection API 的扩展来支持注解处理
注解是要处理的,否则,它将没有任何意义。
Java 5 扩展了 Reflection API,以帮助你创建自己的注释处理工具。
例如,Class 声明一个 Annotation[] getAnnotations()方法,该方法返回一个 java.lang.Annotation 实例数组,该实例描述由 Class 对象描述的元素上存在的注解。
下面示例提供了一个简单的程序,该应用程序加载一个类文件,询问其 ToDo 注解的方法,并输出每个找到的注解的组件:
验证仅指定了一个命令行参数(标识类文件)后,main()通过 Class.forName()加载类文件,调用 getMethods()返回 java.lang.reflect.Method 对象的数组 识别类文件中的所有公共方法,并处理这些方法。
编译此源代码(javac AnnProcDemo.java)。
在运行该应用程序之前,你需要一个合适的类文件,该文件的公共方法上带有 @ToDo 批注。
编译修改后的AnnDemo
,
会得到如下输出:
标准注解类型
在 Java 5 中,与 Target,Retention,Documented 和 Inherited 一起引入了 java.lang.Deprecated,java.lang.Override 和 java.lang.SuppressWarnings。
这三种注解类型被设计为仅在编译器上下文中使用,这就是为什么将其设置为 SOURCE 的原因。
Deprecated
用于注解那些将要被弃用的元素,当程序不建议使用时,编译器会发出警告消息。
Override
为重写其父类对应方法的子类方法添加注解。当子类方法未重新父类方法时,编译器将报告错误。
SuppressWarnings
用于注解应用程序元素(以及这些应用程序元素中包含的所有元素),其中任何已命名的编译器警告(例如,unchecked)都应该被禁止。
干货推荐
为了方便大家,我花费了半个月的时间把这几年来收集的各种技术干货整理到一起,其中内容包括但不限于 Python、机器学习、深度学习、计算机视觉、推荐系统、Linux、工程化、Java,内容多达 5T+,我把各个资源下载链接整理到一个文档内,
所有干货送给大家,希望能够点赞支持一下!
版权声明: 本文为 InfoQ 作者【Jackpop】的原创文章。
原文链接:【http://xie.infoq.cn/article/933838c99d70b1777ae1a6516】。文章转载请联系作者。
评论