阅读 102

注解的基本使用

一、相关概念

1、注解是什么? 是以@开头的,给代码添加的一些信息。类、接口、方法、变量、方法中的参数、构造方法等都可以被注解。
注解怎么运行的? 注解可以被编译器、程序运行时或者其他工具使用。可以让编译器检查代码或者生成代码(编译时注解),可以保留到运行时来改变程序的行为(运行时注解)。

二、注解的分类:

分为内置注解和元注解。内置注解包括@Override、@Deprecated、@SuppressWarnings等常用注解,元注解是用来生成注解的注解。。。
1、内置注解

  • @Override 修饰方法。对覆盖超类的方法进行标记,如果被标记的方法在超类中没有,编译器就会报错。
  • @Deprecated 修饰类、方法、字段、参数。表示对应的代码已经过时,不应该使用。
  • @SuppressWarnings 压制Java编译警告。
  • @SafeVarargs 用在参数长度可变的方法或构造方法上,且方法必须声明为static或final Override 注解源码:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}   
复制代码

其中有三个注解 @Target、@Retention 、@interface,分别代表什么?
定义注解跟定义接口类似,需要使用@interface ,然后定义@Target(注解修饰的范围),定义@Retention (保留到啥时候)

2、元注解: 元注解有以下几种:

  • @Target:注解所修饰的对象范围
  • @Inherited:注解可以被继承
  • @Documented:注解可被JavaDoc工具记录
  • @Retention:注解保留策略,保留到啥时候
  • @Repeatable:表示一个注解可在同一声明类型(类、属性、方法)多次使用

(1)@Target 修饰对象范围

@Target表示注解的目标,@Override的目标是方法(ElementType.METHOD),ElementType是一个枚举,其他可选值有:

  • TYPE:表示类、接口(包括注解),或者枚举声明
  • FIELD:字段,包括枚举常量
  • METHOD:方法
  • PARAMETER:方法中的参数
  • CONSTRUCTOR:构造方法
  • LOCAL_VARIABLE:本地变量
  • ANNOTATION_TYPE:注解类型
  • PACKAGE:包
  • ElementType.TYPE_PARAMETER:泛型参数的声明
  • ElementType.TYPE_USE:泛型的使用 目标可以有多个,用{}表示 ,例如:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}
复制代码

(2)@Retention 保留策略
@Retention表示注解信息保留到什么时候,取值只能有一个,类型为RetentionPolicy,它是一个枚举,有三个取值:

  • SOURCE:只在源代码中保留,编译器将代码编译为字节码文件后就会丢掉
  • CLASS:注解会被编译器记录在class文件中,但不需要被VM保留到运行时
  • RUNTIME:一直保留到运行时,可通过反射获取注解信息 @Override是给编译器检查用的的,所以@Retention是RetentionPolicy.SOURCE。

(3)@Inherited 注解可被继承
定义注解:

@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {

}
复制代码

父类使用注解:

@MyTest
public class Parent {

}
复制代码

子类继承父类:

public class Child extends Parent {

}
复制代码

判断子类是否有同样的注解:

   public static void main(String[] args) {
        //child类是否有MyTest的注解
        System.out.println(Child.class.isAnnotationPresent(MyTest.class));
    }
复制代码

结果是true,因为使用了@Inherited,注解被子类继承下来了
(4)定义参数

@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {
  boolean optional() default false;
}
复制代码

有一个参数optional,默认值为false。如果定义了参数且没有提供默认值,在使用注解时必须提供具体的值,不能为null。

三、定义注解

1、定义编译时注解:

@Retention(RetentionPolicy.CLASS)
public @interface Parent {
    String name();
    int age() default 20;
}
复制代码

注解使用:

public class ParentTest {
    @Parent(name = "zhangsan", age = 12)
    public void say(){

    }
}
复制代码

2、定义运行时注解:

@Retention(RetentionPolicy.RUNTIME)
public @interface Parent {
    String name();
    int age() default 20;
}
复制代码

四、注解处理器:

注解是如何被解析出来的?针对运行时注解会采用反射机制,针对编译时注解会采用AbstractProcessor处理。
1、运行时注解处理:

//获取所有的注解
public Annotation[] getAnnotations()
//获取所有本元素上直接声明的注解,忽略inherited来的
public Annotation[] getDeclaredAnnotations()
//获取指定类型的注解,没有返回null
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
//判断是否有指定类型的注解
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
复制代码

通过反射获取注解的信息:

   public static void main(String[] args) {
        Method[] declaredMethods = ParentTest.class.getDeclaredMethods();
        for (Method method : declaredMethods) {
            Parent annotation = method.getAnnotation(Parent.class);
            System.out.println("name:" + annotation.name());
        }
    }
复制代码

通过调用getAnnotation方法返回指定类型的注解对象Parent,然后调用Parent的name方法获取name元素的值。
2、编译时注解处理:
编译时注解的核心依赖APT(Annotation Processing Tools)实现,原理是添加完注解后,在编译时编译器会检查AbstractProcessor的子类,并且调用该类型的process函数,可以在该方法进行相应的处理。
具体用法未完待续....

文章分类
Android