自从 Java 5.0 版本引入注解后,它就成为 Java 平台中非常重要的一部分。在日常开发过程中时常可以看到 @Override,@Deprecated 这样的注解,在使用 Butterknife 减少 findViewById 这样的重复劳动时也经常用到 @BindView 注解。下面几篇文章将讲述注解的基本概念,注解处理器的使用以及 Android中 的注解:
探索注解之 APT 编译时处理注解 (鸿神写编译时注解的文章,非常好,所以自己就不再重复写了)
什么是注解?
注解(Annotations),就是元数据,一种描述程序代码的数据,并且对程序运行没有任何影响。
例如下面这段代码:
|
|
在上面的代码中,我重写了 toString() 方法并使用了 @Override 注解。但是即便我不使用 @Override 注解标记代码,程序也能正常运行。那么这个注解有什么意义?@Override 告诉编译器这个方法是重写父类的方法,如果父类中没有该方法,编译器就会报错,提示没有重写父类中的方法。如果我不小心把 toString 写成 toStrings,而且也没有使用 @Override 注解的话,程序依然可以运行,但是跟之前期望就会大有不同。
注解有下面几种作用:
- 为编译器提供信息 – 注解可以被编译器用来检测错误或抑制警告
- 编译时和部署时处理 – 一些工具可以根据注解信息生成代码,XML 文件等等,例如 APT(Annotation Processing Tool)
- 运行时处理 – 有些注解可以在运行时通过反射获取
在引入注解之前,开发人员通常使用标记 interfaces,注释或者 javadoc 定义元数据,没有统一的一种方式。注解定义了一种标准的描述元数据的方式,注解是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由 JSR-175 标准选择用来描述元数据的一种工具。
Java 内置注解
在了解注解的定义后,先看看我们熟悉的 Java 内置的注解:
@Deprecated – 意味着标记的元素这已经弃用了,如果使用了 @Deprecated 标记的元素编译器会提示警告。Java 推荐在 Javadoc 中提供弃用的原因并提供替代的方案.
Date 的 pare 方法
@Override – 标记该方法是重写父类的,当父类的该方法移除或修改时,编译器会提供错误信息。
@SuppressWarnings – 告诉编译器忽略某些特定的警告。
|
|
@SafeVarargs – 忽略”堆污染”警告,Java 7 引入的
@FunctionalInterface – Java 8 引入的,用来表示接口是函数式接口
元注解
在上面 Java 自带注解或者自定义注解时,会发现应用于注解本身的注解,这就是元注解。这些元注解都定义在java.lang.annotation
包中。
@Retention – 定义了注解的生命周期
RetentionPolicy.SOURCE - 只能在源文件中保留,被编译器丢弃
RetentionPolicy.CLASS - 可以在 .Class 文件中保留,被 JVM 丢弃,这也是默认值
RetentionPolicy.RUNTIME - 可以在运行时保留
@Documented – 可以使用 javadoc 或类似工具文档化
@Target – 指定注解可修饰的元素范围,即可以标记在哪里(方法上、类上等等),如果不使用 @Target 注解,可以修饰任何元素。下面是取值列表:
ElementType.ANNOTATION_TYPE - 描述其他注解
ElementType.CONSTRUCTOR - 描述构造器
ElementType.FIELD - 描述类属性(包括 enum 常量)
ElementType.LOCAL_VARIABLE - 描述局部变量
ElementType.METHOD - 描述方法
ElementType.PACKAGE - 描述包
ElementType.PARAMETER - 描述参数
ElementType.TYPE - 描述类、接口(包括注解)、枚举 enum
@Inherited – 表明被标注的注解可被子类继承
@Repeatable – Java 8 引入,表明被标记的注解可以在一个声明或类型中使用多次
自定义注解
自定义注解有点类似写接口,只是interface
关键字需要前缀@
。我们可以声明一个描述类属性的注解,下面是具体例子:
|
|
上面定义了 @Column 注解,有三个成员变量,成员变量的声明类似无参方法的声明,定义注解的规则如下:
注解可以用元注解标记,指定可应用的元素类型,或确定生命周期等
注解的属性用类似无参方法的声明,不能带有任何参数
注解的属性类型只能是基本数据类型、String、Enum、注解以及相应的数组
注解的属性可以有默认值,非基本数据类型的值不能为
null
如果只有一个属性,建议命名为 “value”,使用时就无需标明属性名
Java 注解解析(运行时)
上面我们已经定义了 @Column 注解,下面是注解使用的例子:
|
|
之前说过注解不会影响代码的运行,因此必须配合相应的注解处理工具,否则注解跟注释没什么分别。根据 @Retention 指定的值不同,有不同的处理方式。声明为 RetentionPolicy.CLASS 时,可以用 javac 内置的 APT(Annotation Processing Tool)在编译时扫描处理注解,而声明为 RetentionPolicy.RUNTIME 时,可以用反射 API 的 AnnotatedElement 接口解析注解。
下面的代码示例是用反射解析上面 Bookmarks 类中的注解:
|
|
运行结果如下:
|
|
参考文章: