自定义注解及注解的使用

注解简介

注解的英文就是 Annotation,是在JDK 1.5之后引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

注解就是给 java 代码加上一个标识规则,javac编译器在编译时就会去检测应用了该注解类的类是否符合标识规则,来约束编码规范。

元注解

Java目前只内置了三种标准注解,以及四种元注解。

1、@Target
表示支持注解的程序元素的种类,注解该用于什么地方,ElementType 注解修饰的元素类型,使用ElementType枚举类来表示:

CONSTRUCTOR:构造器的声明
FIELD:域声明(包括enum实例)
LOCAL_VARIABLE:局部变量声明
METHOD:方法声明
PACKAGE:  包声明
PARAMETER:参数声明
TYPE:类,接口(包括注解类型)或enum声明
ANNOTATION_TYPE:注解类型声明
TYPE_PARAMETER:类型参数声明  从jdk1.8开始  、 hide1.8
 TYPE_USE:类型的使用 从jdk1.8开始 、 hide1.8

2、@Retention
表示保留时间的长短,需要在什么级别保存该注解信息, RetentionPolicy参数包括:

SOURCE:注解将被编译器丢弃。
CLASS:注解在class文件中可用,但会被VM丢弃。
RUNTIME:VM将在运行期也保留注解,因此可通过反射机制读取注解的信息。

RetentionPolicy 它是枚举类,分别简单了解三种类型:

  • RetentionPolicy.SOURCE

    当注解保留时期为 SOURCE 时:表示注解信息只会被保留到源码和编译期间,当javac编译器编译源代码后会将该注解去除,但是在编译期间,注解处理器是是可以处理这些注解的,在编译完成之后,这些注解信息就没有了。

  • RetentionPolicy.CLASS

    当注解保留时期为 CLASS 时:表示注解信息会被保留到编译时期,该注解信息会被编译到.class文件中,但不会被虚拟机加载到内存中,保留到 Javac 将.java文件编译成 .class文件,但是不会随着 ClassLoader 将该.class文件加载到内存中。

  • RetentionPolicy.RUNTIME

    .当注解保留时期为 RUNTIME 时:表示注解信息会被保留到"运行时期",这是可以通过反射获取该注解的信息。Javac会将源文件.java编译为.class文件,然后通过 ClassLoader 将.class文件加载到内存中成为字节码,这时就可以通过反射来获取对应的注解信息。

3、@Documented
将此注解包含在Javadoc中。 表示使用该注解的元素应被javadoc或类似工具文档化,它应用于类型声明,类型声明的注解会影响客户端对注解元素的使用。如果一个类型声明添加了Documented注解,那么它的注解会成为被注解元素的公共API的一部分。

4、@Inherited
允许子类继承父类中的注解。表示一个注解类型会被自动继承,如果用户在类声明的时候查询注解类型,同时类声明中也没有这个类型的注解,那么注解类型会自动查询该类的父类,这个过程将会不停地重复,直到该类型的注解被找到为止,或是到达类结构的顶层(Object)。

在开发中最最常用几个注解

  • @Override 约束方法必须从父类中覆写

    复写Object 中的equals(Object obj)时很容将参数类型定义为当前类的类型,如果没有添加@Override的话那系统会认为这个方法并不是继承至Object的equals方法,那么使用两个对象进行比较就会出现问题。

  • @Deprecated 约束元素已经过期,不建议使用。

  • @SuppressWarning 压制警告提示。

通过例子验证如何使用注解

通过编写代码,更能了解注解,对注解的概念更能清楚。首先,编写一个自定义注解类BindView

/**
 * @Author lu an
 * Create Date is  2019/10/9
 * Des  绑定view的注解
 */
//保存的级别到运行时期
@Retention(RetentionPolicy.RUNTIME)
//目标->field 字段
@Target(ElementType.FIELD)
public @interface BindView {
    int viewId() default 0;//默认0
    boolean onClick() default false;//default表示默认
    boolean onLongClick() default false;//default表示默认
}

default表示默认值 ,也可以不编写默认值的。下面实现一个注解工具实现类:

/**
 * @Author lu an
 * Create Date is  2019/10/9
 * Des  注解工具类
 */
public class InjectUtils {
    public static void init(Activity ctx){
        //获取Class对象
        Class clazz = ctx.getClass();
        //获取字段变量
        Field[]fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            //判断是否绑定了BindView
            if(field.isAnnotationPresent(BindView.class)){
                //获取到BindView对象
                BindView bindView = field.getAnnotation(BindView.class);
                //通过getAnnotation方法取出标记了注解的字段viewId
                int viewId = bindView.viewId();
                if(viewId>0){
                    field.setAccessible(true);
                    //获取到view
                    View childView = ctx.findViewById(viewId);
                    if (childView != null) {
                        //绑定的点击监听事件
                        if (bindView.onClick() && ctx instanceof View.OnClickListener) {
                            childView.setOnClickListener((View.OnClickListener) ctx);
                        }
                        if (bindView.onLongClick() && ctx instanceof View.OnLongClickListener) {
                            childView.setOnLongClickListener((View.OnLongClickListener) ctx);
                        }
                    }
                }
            }
        }
    }
}

通过反射方式,通过getAnnotation方法取出标记了注解的字段viewId,得到了viewId后,通过上下文findViewById找到对应的View控件,bindView.onClick() 对应的@BindView(viewId = R.id.btn_annotation,onClick = true)中的onClick值,ctx instanceof View.OnClickListener则表示Activity是否实现了点击接口.

下面就编写一个Activity类实现注解绑定view及其点击事件:

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    //绑定view控件 和 绑定view的点击事件
    @BindView(viewId = R.id.btn_annotation,onClick = true)
    Button mBtnAnnotation;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //注解初始化
        InjectUtils.init(this);
    }
    @Override
    public void onClick(View v) {
        Toast.makeText(this,"Binding View Success",Toast.LENGTH_SHORT).show();
    }
}

@BindView(viewId = R.id.btn_annotation,onClick = true)表示绑定view控件 和 绑定view的点击事件,如果只单单绑定view,如@BindView(viewId = R.id.btn_annotation)即可。

当你点击Button时就会弹出Binding View Success。

总结

  • 了解注解的基本概念
  • 了解自定义注解与元注解,注解参数与默认值
  • 如何使用注解,通过反射,在运行时动态获取注解信息。当然注解的注解不紧紧只有反射,还有APT(编译时注解处理器)和插桩(编译后处理筛选)

你可能感兴趣的:(自定义注解及注解的使用)