第二章注解深入浅出
一、注解 Annotations
1.1 注解
注解是一种将元数据附加到代码中的方法。
注解(Annotation),也叫做元数据,一种代码级别的说明。 它是 JDK 1.5 及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明、注解。
Java 注解:声明注解需要在类前面使用 @interface 关键字:
public @interface MyAnnotation {
}
Kotlin 注解:声明注解需要在类前面使用 annotation 关键字:
annotation class MyAnnotation
Android 注解的好处:
- 简化代码,提高开发效率(不一定提高运行效率)
- 更早的发现程序的问题或者错误
- 更好的增加代码的描述能力
- 更加利于我们的一些规范约束
- 提供解决问题的更优解
1.2 元注解
元注解是用来定义其他注解的注解,简单来说,就是注解上的注解。
元注解共有四种:@Rentention、@Target、@Inherited、@Documented。
@Retention:注解保留的声明周期,默认值为 CLASS。对应 RetentionPolicy 的枚举,表示注解在何时生效,可选值有三种
- SOURCE:只在源码中可用(当 Java 文件编译成 Class 文件的时候,注解被遗弃)
- CLASS:在源码和字节码中可用(Jvm 加载 Class 文件的时候被遗弃,这是默认的声明周期)
- RUNTIME:在源码、字节码和运行时均可用(Jvm 加载到 Class 文件之后,仍然存在)
SOURCE < CLASS < RUNTIME,前者能作用的地方后者一定也能作用。
@Target:注解对象的作用范围,对应 ElementType 枚举,明确了注解的有效范围。ElementType 枚举中包括:
- TYPE:类、接口、枚举、注解类型
- FIELD:类成员(构造方法、方法、成员变量)
- METHOD:方法
- PARAMETER:方法参数
- CONSTRUCTOR:构造函数
- LOCAL_VARIABLE:局部变量
- ANNOTATION_TYPE:注解
- PACKAGE:包
- TYPE_PARAMETER:类型参数
- TYPE_USE:类型使用声明
@Inherited:注解是否可以被继承,默认为 false。用 @Inherited 声明出来的注解只对类有效,对方法/属性无效。
@Document:是否会保存到 Javadoc 文档中。
现在来看下 @Override 注解的源码:
package java.lang;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.METHOD})
public @interface Override {
}
分析一下,
@Retention(RetentionPolicy.SOURCE):表示 @Override 注解只在源码中使用,
@Target({ElementType.METHOD}):表示 @Override 注解作用的范围在方法中,也就是用来修饰方法的。
1.3 自定义注解
自定义注解的时候,需要使用到元注解来定义我们自定义的注解。
举个例子讲解。
package com.example.demo;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME)
@Target(FIELD)
public @interface TestAnnotation {
String value();
String[] value2() default "value2";
}
注解值的写法:类型 参数名() default 默认值;
其中默认值是可选的,可以定义,也可以不定义。
处理运行时注解
Rentention 的值为 RUNTIME 时,注解会保留到运行时,因此使用反射来解析注解。
还是用上一步的注解 @TestAnnotation,解析示例如下:
package com.example.demo;
import java.lang.reflect.Field;
public class Demo {
@TestAnnotation("Hello Annotation")
private String testAnnotation;
public static void main(String[] args) {
try {
// 获取要解析的类
Class clazz = Class.forName("myAnnotaiton.Demo");
// 拿到所有 Filed
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
// 获取 Field 上的注解
TestAnnotation testAnnotation = field.getAnnotation(TestAnnotation.class);
if (null != testAnnotation) {
// 获取注解值
String value = testAnnotation.value();
System.out.println("value = " + value);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}