注解

作者: crossroads | 来源:发表于2021-02-19 18:10 被阅读0次

    前言

    注解,提高开发效率,不一定提高程序运行效率。今天就学习下简化代码的注解把。
    先看个示例,对照着学

    @Documented
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Subscribe {
        int code() default -1;
        ThreadMode threadMode() default ThreadMode.CURRENT_THREAD;
    }
    

    一、注解的语法和定义形式

    1.以@interface关键字定义

    1. 注解包含成员,成员以无参数的方法形式声明,其方法名和返回值定义了该成员的名字和类型。
    2. 成员赋值通过@Annotation(name=value)的形式
    3. 注解通过元注解@Retention、 @Target、@Inherited、@Documented来表明注解的生命周期、注解的修饰目标、直接可否被继承、注解被javadoc此类的工具文档化。
      上面的示例,可以看出:
    4. @Retention的值为RetentionPolicy.RUNTIME,@Target的值为ElementType.METHOD
    5. 成员名称为code,类型为int,默认值-1
      注解元素的默认值
      注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。因此, 使用空字符串或0作为默认值是一种常用的做法。这个约束使得处理器很难表现一个元素的存在或缺失的状态,因为每个注解的声明中,所有元素都存在,并且都具有相应的值,为了绕开这个约束,我们只能定义一些特殊的值,例如空字符串或者负数,表示某个元素不存在,在定义注解时,这已经成为一个习惯用法

    二、元注解@Rententation @Target详解

    1. @Retentation的值是enum类型,有三个可取值

    public enum RetentionPolicy {
        SOURCE,//注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃
        CLASS,//注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期
        RUNTIME;//注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
        private RetentionPolicy() {
        }
    }
    

    这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。
    一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解
    2. @Target的值也是enum类型,有八个可选值

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.ANNOTATION_TYPE})
    public @interface Target {
        ElementType[] value();
    }
    
    public enum ElementType {
        TYPE,//类、接口(包括注解类型)或枚举 
        FIELD,// 适用field属性,也包括enum常量
        METHOD,//适用方法
        PARAMETER,//方法参数
        CONSTRUCTOR,//构造函数
        LOCAL_VARIABLE,//局部变量
        ANNOTATION_TYPE,//注解
        PACKAGE,//包
        TYPE_PARAMETER,//类型参数声明
        TYPE_USE;//使用类型
    
        private ElementType() {
        }
    }
    
    

    三、注解解析

    1. 找到类对应的所有属性或方法(看自定义的注解是定义方法上还是属性上)
    2. 找到添加了注解的属性或者方法, 做注解需要自定义的一些操作
      1. 如何找到类对应的属性和方法?
      通过Class对象,例如
            Class<?> subClass = obj.getClass();
            Method[] methods = subClass.getDeclaredMethods();
    

    这样就可以获取当前类所有的方法,即包括public、private和protected,注意不包括父类。
    接下来,看看我们通过class对象都能获得什么?

      /**
         * 包名加类名
         */
        public String getName();
    
        /**
         * 类名
         */
        public String getSimpleName();
    
        /**
         * 返回当前类和父类层次的public构造方法
         */
        public Constructor<?>[] getConstructors();
    
        /**
         * 返回当前类所有的构造方法(public、private和protected)
         * 不包括父类
         */
        public Constructor<?>[] getDeclaredConstructors();
    
        /**
         * 返回当前类所有public的字段,包括父类
         */
        public Field[] getFields();
    
        /**
         * 返回当前类所有申明的字段,即包括public、private和protected,
         * 不包括父类
         */
        public native Field[] getDeclaredFields();
    
        /**
         * 返回当前类所有public的方法,包括父类
         */
        public Method[] getMethods();
    
        /**
         * 返回当前类所有的方法,即包括public、private和protected,
         * 不包括父类
         */
        public Method[] getDeclaredMethods();
    
        /**
         * 获取局部或匿名内部类在定义时所在的方法
         */
        public Method getEnclosingMethod();
    
        /**
         * 获取当前类的包
         */
        public Package getPackage();
    
      /**
         * 获取当前类的直接超类的 Type
         */
        public Type getGenericSuperclass();
    
        /**
         * 返回当前类直接实现的接口.不包含泛型参数信息
         */
        public Class<?>[] getInterfaces();
    
        /**
         * 返回当前类的修饰符,public,private,protected
         */
        public int getModifiers();
    

    2. 如何找到添加了注解的属性或方法
    Field和Method都实现了AnnotatedElement接口,通过这个接口就可以,例如

    for (Method method : methods) {
    // 指定类型Subscribe的注释是否存在于此元素上
        if (method.isAnnotationPresent(Subscribe.class)) {
    // 返回该元素上存在的指定类型的注解
          Subscribe sub = method.getAnnotation(Subscribe.class);
           int code = sub.code();//获得code值
            //获得方法的参数类型
            Class[] parameterType = method.getParameterTypes();
        }
    }
    

    四、使用注解

        @Subscribe(threadMode = ThreadMode.MAIN)
        public void eventBus(EventEntity obj) {
        }
    
    五、示例

    findviewById的注解

    @Documented
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CBindView {
        @IdRes int value();
    }
    
    
    public class CButterKnife {
        private CButterKnife() {
        }
    
        public static void bind(@NonNull Activity source) {
            View sourceView = source.getWindow().getDecorView();
            Field[] declaredFields = source.getClass().getDeclaredFields();//不包含父类的成员变量
            for (Field field : declaredFields) {
                if (field.isAnnotationPresent(CBindView.class)) {//注释是否存在于此元素上
                    CBindView bindView = field.getAnnotation(CBindView.class);
                    if (bindView != null) {
                        field.setAccessible(true);//允许修改反射属性
                        try {
                            //将source对象的这个field属性设置为新值sourceView.findViewById(bindView.value())
                            field.set(source, sourceView.findViewById(bindView.value()));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
    
            }
        }
    }
    
    public class MainActivity2 extends AppCompatActivity {
        @CBindView(R.id.title)
        private TextView textView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main2);
            CButterKnife.bind(this);
            textView.setText("测试");
        }
    }
    

    这里顺便加个知识点吧,反射经常用到的除了field.set还有一个method.invoke,这个方法如method.invoke(action,args)调用action对象的myMethod方法,传参为args。

    后记

    整理总结于网址

    相关文章

      网友评论

          本文标题:注解

          本文链接:https://www.haomeiwen.com/subject/ngrnxltx.html