什么是注解
注解(Annotation)也叫元数据,它是一种对代码级别的说明。JDK1.5开始引入的一种特性。与类、接口、枚举属同一级别,它可以声明在包、类、接口、字段、方法、局部变量、方法参数的前面,用于对这些代码元素进行说明。
注解的用途
注解常见的用途有编译时代码校验(如:@Override等)、编译时自动生成代码(如Arouter、EventBus等)、运行时获取注解信息等。
Java常见的自带注解
- @Override:它用于编译时多态重写语法进行校验,校验不通过则编译失败
- @Deprecated:它用于对类、接口、字段、方法等进行说明,说明这些元素已经过时或者在新版本中已经被抛弃,不推荐再使用,而是使用新的方案。
- @SuppressWarnings:它用于对类、接口、方法等进行说明,为的是阻止编译器发出某些警告。
自定义注解
注解定义
注解的用途还是挺广泛的,可以对代码解耦、面向切面编程(AOP)等,所以我们可以根据需要自行创建注解。定义注解与定义接口的语法差不多,不同在于注解在interface前面多加了个@符合,如下所示:
public @interface TestAnnotation {
int value() default 1;
}
注解实际上也属于接口,它继承的是java.lang.annotation.Annotation,我们通过javap进行反编译可以得到印证:
E:\project\demo\test1\app\src\main\java\com\wyx\test1\annotation>javap TestAnnotation.class
Compiled from "TestAnnotation.java"
public interface com.wyx.test1.annotation.TestAnnotation extends java.lang.annotation.Annotation {
public abstract int value();
}
注解属性
注解的属性相对比较特殊,它是以抽象方法的形式进行定义,设置默认值在其后面加上default xxx即可,如上面的代码举例。
注解属性类型
注解属性支持的类型有:
- 基本数据类型(boolean、byte、short、int、long、float、double、char)
- String
- 枚举
- 注解
- 上述类型的数组
注意:不支持class、interface、基本类型的装箱类(如Byte、Integer等)
注解的使用
定义好注解之后,就可以应用在程序元素的上面,用于的元素进行说明,如:
@TestAnnotation
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
上面的代码中@TestAnnotation注解应用在MainActivity类的上面,对MainActivity类进行注解说明。
使用注解分为标记注解、单值注解、多值注解:
标记注解
标记注解指没有属性的注解,一般用于对元素进行标记,标记元素拥有此注解,如@Override:
public @interface Override {
}
使用标记注解时,直接在元素上加上标间注解即可,如@Override:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
单值注解
单值注解指只有一个属性的注解,一般以value()作为属性,如:
@Retention(RetentionPolicy.CLASS)
public @interface TestAnnotation {
int value() default 1;
}
使用单值注解时,如果属性有默认值且使用默认值,则可以省略对属性的赋值。如:
@TestAnnotation
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
此时MainActivity的注解@TestAnnotation中的属性value值使用的是默认值即1。
如果属性没有默认值,则在使用是必须对属性进行赋值。
如果对属性进行赋值,则在使用注解时,加上括号,括号内使用key=value(其中key是注解定义的属性名称,value是属性类型的值)的形式进行赋值,如:
@TestAnnotation(value = 2)
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
此时MainActivity的注解@TestAnnotation中的属性value被赋值为2。
多值注解
多值注解指有多个属性的注解,如下所示:
@Retention(RetentionPolicy.CLASS)
public @interface TestAnnotation {
int value() default 1;
String name() default "";
int[] array();
}
使用多值注解时,如果属性有默认值且使用默认值,则可以忽略对属性的赋值;如果属性没有默认值则必须对属性进行赋值,多属性的赋值与单属性赋值差不多,也是在括号中进行赋值,但是以key1=value1, key2=value2的形式,如下所示:
@TestAnnotation(value = 2, name = "test", array = {1, 2, 3})
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
上述对时MainActivity 的注解@TestAnnotation的属性进行了赋值,此时MainActivity 的注解@TestAnnotation的属性value值为2,属性name值为test,属性array的值为[1,2,3]。
注意如果属性是数组的形式,那么在对赋值时需要采用花括号进行赋值(如{xxx, xxx})。
元注解分类
元注解是指用来描述注解的注解。一般元注解是用来限定注解的使用范围、生命周期等。
jdk定义了如下四种类型的元注解:
元注解 | 描述 |
---|---|
@Target | 指定被修饰的注解的作用范围 |
@Retention | 指定被修饰的注解的生命周期 |
@Documented | 指定被修饰的注解是可以被Javadoc等工具文档化 |
@Inherited | 指定被修饰的注解对程序元素进行修饰说明时,是可以被子类继承的 |
@Target
@Target 指定被修饰的注解的作用范围。
@Target是一个单值注解,属性名称是value,属性类型为ElementType[],表示注解可以限定于某一类型元素,也可限定作用于某几种类型元素。@Target的源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Target {
ElementType[] value();
}
@Target可取的属性值可以在ElementType的源码中查看:
属性值 | 描述 |
---|---|
TYPE | 限定作用于:类、接口、注解、枚举的声明 |
FIELD | 限定作用于:属性(包括枚举常量)的声明 |
METHOD | 限定作用于:方法 |
PARAMETER | 限定作用于:形式参数的声明 |
CONSTRUCTOR | 限定作用于:构造函数的声明 |
LOCAL_VARIABLE | 限定作用于:局部变量的声明 |
ANNOTATION_TYPE | 限定作用于:注解类型的声明 |
PACKAGE | 限定作用于:包声明 |
TYPE_PARAMETER | 限定作用于:类型参数声明 |
TYPE_USE | 限定作用于:类型的使用 |
一般常用的是TYPE 、FIELD 、METHOD 、PARAMETER
@Retention
@Retention 指定被修饰的注解的生命周期。
@Retention也是一个单值注解,其属性名称为value,属性类型为RetentionPolicy,@Retention的源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Retention {
RetentionPolicy value();
}
@Retention可取的属性值可以在RetentionPolicy的源码中查看:
属性值 | 描述 |
---|---|
SOURCE | 注解只保留在源码阶段,编译器编译之后就会被丢弃忽视。 |
CLASS | 注解被编译器编译并保留在class文件中,但在JVM运行时会被丢弃忽视。 |
RUNTIME | 注解会被保留到运行时,它会被加载进JVM中,可以在程序运行时通过反射获取到注解信息 |
注解自动生成代码
注解自动生成代码的原理是:将注解生命周期限定至少为CLASS时,再通过编译时APT(Annotation Process Tool,编译期注解处理器)技术获取源码中的注解的信息,根据规则自动生成代码。
此技术将会在后面的“Arouter之自动生成代码原理”进行详细的介绍。
运行时获取注解信息
运行时获取注解信息主要是在运行期间通过反射获取注解信息,这里需要将注解的生命周期限定为RUNNING。
下列代码是运行时获取注解信息的例子:
@TestAnnotation(value = 2, name = "test", array = {1, 2, 3})
public class TestAn {
public static void main(String[] args) {
Log.d("TestAn", "in main method");
Class claz = TestAn.class;
TestAnnotation an = (TestAnnotation) claz.getAnnotation(TestAnnotation.class);
System.out.printf("TestAnnotation value: %d, name: %s\n", an.value(), an.name());
}
}
网友评论