Annotation
概念
能够添加到 Java 源代码的语法元数据。类、方法、变量、参数、包都可以被注解,可用来将信息元数据与程序元素进行关联。Annotation 中文常译为“注解”。
注解不会改变程序被编译的方式。不管有没有被注解,Java 编译器都会产生相同的虚拟机指令。
注解元素
- 基本类型值
- String(字符串)
- Class对象
- enum(枚举)实例
- 注解
- 由前面几种类型组成的数组(但不能是数组的数组)
例如:
@MyAnnotation(showImg = true,
loginName = "nvgtor",
testCase = CacheTest.class,
status = MyAnnotation.Status.CONFIRMED)
元素可以有默认值。
注解的类型可以分为两类:声明和类型用途
注解声明
声明注解可以出现在如下情况里:
- 类(包括 enum)和接口(包括注解接口)
- 方法
- 构造函数
- 实例变量(包括 enum常量)
- 局部变量(包括在 for 中声明的局部变量)
- 参数变量和 catch 子句参数
- 类型参数
- 包
注解类型用途
例如:
// 断言参数 userId 不能为 null
public User getUser(@NonNull String userId)
//所有字符串都是非空
List<@NonNull String>
类型用途注解可以出现在一下地方:
- 带泛型类型的参数: List<@NonNull String>
- 在数组的任何位置: @NonNull String[][] words(words[i][j] 非空),String @NonNull [][] words(words非空), String[] @NonNull [] words(words[i] 非空)
- 父类和被实现接口
- 构造函数调用
- 嵌套类型
- 。。。
定义注解
每个注解必须通过一个注解接口(以 @interface 语法)声明。接口的方法对应注解的元素。如:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
public String key();
public String user();
}
@interface 声明会创建一个实际的 java 接口。处理注解的工具会接受实现了注解接口的对象。注解接口中的元素声明实际上是方法声明。注解接口的方法没有参数,不能有 throws 语句,也不能是泛型的。
- 所有方法没有方法体,没有参数没有修饰符,实际只允许 public & abstract 修饰符,默认为 public ,不允许抛异常
- 方法返回值只能是基本类型,String, Class, annotation, enumeration 或者是他们的一维数组
- 若只有一个默认属性,可直接用 value() 函数。一个属性都没有表示该 Annotation 为 Mark Annotation
元注解
- @Documented 是否会保存到 Javadoc 文档中
- @Retention 保留时间,可选值 SOURCE(源码时),CLASS(编译时),RUNTIME(运行时),默认为 CLASS,值为 SOURCE 大都为 Mark Annotation,这类 Annotation 大都用来校验,比如 Override, Deprecated, SuppressWarnings
- @Target 可以用来修饰哪些程序元素,如 TYPE, METHOD, CONSTRUCTOR, FIELD, PARAMETER 等,未标注则表示可修饰所有
- @Inherited 是否可以被继承,默认为 false
编译相关的注解
- @Deprecated 不再鼓励使用, 过时的
- @Override 使得编译器检查被注解的方法是否真的重载了一个来自父类的方法。
- @SuppressWarning 告诉编译器抑制一个特定类型的警告。
- @SafeVarargs 用于断言一个方法不损坏它的可变参数。
- @Generated 给代码生成工具使用
- @FunctionalInterface 用来为 lambda 表达式注解转换目标。
一篇很好的博客:
Annotation 示例、概念及作用、分类、自定义、解析,并对几个 Android 开源库 Annotation 原理进行简析。