注解

作者: 壹元伍角叁分 | 来源:发表于2021-05-30 22:52 被阅读0次

一:什么是注解?
注解只是个标签,本身并没有意义,需要结合其他场景使用
1、元注解:

//注解作用的地方:类、方法等,可以使用多个
@Target({ElementType.TYPE, ElementType.FIELD})

//注解保留的时间,三种:
//SOURCE(仅仅保留在源码阶段,被编译器忽略),
//CLASS(被编译器保留、但jvm会忽略),
//RUNTIME(jvm保留,运行时可以使用,通过反射)
@Retention(RetentionPolicy.SOURCE)

//语法检查:IDE或者IDE插件实现检查
@IntDef

2、如果只有一个属性,且名称为value,则使用时可以不用表明名称

@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Lance {
    String value();//如有多个属性,或者名称不为value,则必须注明,否则报错
}

使用:

@Lance("value")
public class MainActivity extends AppCompatActivity {
}

3、可以设定默认值;如未设定,使用时,必须设置

public @interface Lance {
 int index() default 1;
}

使用:

@Lance //不用传参,使用默认值
public class MainActivity extends AppCompatActivity {
}

二、注解的应用场景

级别 技术 说明
源码 APT 在编译器能够获取注解与注解声明的类,包括类中所有成员信息,一般用于生产额外的辅助类
字节码 字节码增强 在编译出class后,通过修改class数据以实现修改代码逻辑目的。对于是否需要修改的区分或者修改为不同逻辑的判断可以使用注解
运行时 反射 在程序运行期间,通过反射技术东台获取注解及其元素,从而完成不同的逻辑判定

1、RetentionPolicy.SOURCE(源码)
APT:Annotation Processor Tools(注解处理程序)
javac收集到所有的注解信息,打包成一个节点(Element),然后调用APT进行

实际使用:
某个方法需要限定方法传入的类型,之前是用枚举实现,但枚举的使用,在生成class文件时,实际上是生成了若干个对象,而一个对象由对象头+成员组成,至少12个字节,比较占用内存。如果需要对应用运行内存进行一个优化,可以考虑使用常量+注解代替枚举类型。

public class InitialPenSize {
    @InitialPenSize
    private int mInitialPenSize;

    public static final int FINE = 0;//细,1f,
    public static final int NORMAL = 1;//正常,2.5f,
    public static final int THICK = 2;//粗, 5f,
    public static final int SUPER_THICK = 3;//超粗,9f

    @Target({ElementType.FIELD, ElementType.PARAMETER})
    @IntDef({FINE, NORMAL, THICK, SUPER_THICK})
    @Retention(RetentionPolicy.SOURCE)
    public @interface InitialPenSize {
    }

    public void setInitialPenSize(@InitialPenSize int penSize) {//限定了传入的类型
        this.mInitialPenSize = penSize;
    }
}

使用:

InitialPenSize initialPenSize = new InitialPenSize();
initialPenSize.setInitialPenSize(InitialPenSize.FINE);

2、RetentionPolicy.CLASS(字节码)
字节码增强,在字节码中写代码

3、RetentionPolicy.RUNTIME(运行时)
(1)注解+反射完成findViewById,Butter Knife原来的实现方式:

class InjectViewHelper {
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface InjectView {
        @IdRes int value();
    }

    public static void inject(Activity activity) {
        Class<? extends Activity> aClass = activity.getClass();
        //Field[] fields = aClass.getFields();//获取自己及其父类的所有成员(不包括private,只能是public)
        Field[] declaredFields = aClass.getDeclaredFields();//获取自己的成员(包括private,不包括父类的)

        //那怎么获取到父类的private成员属性呢?
        //Field[] parentDeclaredFields = aClass.getSuperclass().getDeclaredFields();

        for (Field field : declaredFields) {
            if (field.isAnnotationPresent(InjectView.class)) {//判断属性是否被InjectView注解声明
                InjectView annotation = field.getAnnotation(InjectView.class);
                int valueId = annotation.value();//获取到注解上设置的值

                View viewById = activity.findViewById(valueId);
                field.setAccessible(true);//设置访问权限,允许操作private属性
                try {
                    field.set(activity, viewById);//赋值
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

使用:

public class MainActivity extends AppCompatActivity {
 @InjectViewHelper.InjectView(R.id.text_view)
    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        InjectViewHelper.inject(this);//初始化
        mTextView.setText("设置成功");
}

(2)通过注解+反射实现页面跳转的参数注入

//如果页面跳转需要携带多种参数,则在SecondActivity中会获取多次
 StudentBean[] studentBeanArray = {new StudentBean(13), new StudentBean(14)};
 Intent intent = new Intent(MainActivity.this, SecondActivity.class)
                .putExtra("extra_string1", "extra1")
                .putExtra("extra_boolean", true)
                .putExtra("extraInt", 100)
                .putExtra("extra_student_bean", new StudentBean(10))
                .putParcelableArrayListExtra("extra_student_list", extraStudentBeanList)
                .putExtra("extra_student_array", studentBeanArray);
 startActivity(intent);
class InjectIntentHelper {
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)//运行时
    public @interface InjectIntent {
        String extraName() default "";//设置默认值,如果传入的key和变量名一样,可以不写
    }
    
    public static void inject(Activity activity) {
        Bundle extras = activity.getIntent().getExtras();
        Class<? extends Activity> aClass = activity.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            boolean annotationPresent = field.isAnnotationPresent(InjectIntent.class);
            if (annotationPresent) {
                InjectIntent annotation = field.getAnnotation(InjectIntent.class);
                String extraName = annotation.extraName();

                //如果传入的key和变量名一样,且未声明,这边则去判断是否有值,没有则去获取变量名
                String extraKey = TextUtils.isEmpty(extraName) ? field.getName() : extraName;
                Object object = extras.get(extraKey);

                //这边需要注意:如果传入的类型是实现了Parcelable的对象数组,则需要特殊处理一下,其他类型不行
                Class<?> type = field.getType();
                Class<?> componentType = type.getComponentType();
                //判断是否是array数组并且是数组中对象实现了Parcelable接口
                if (type.isArray() && Parcelable.class.isAssignableFrom(componentType)) {
                    Object[] objectArray = (Object[]) object;
                    object = Arrays.copyOf(objectArray, objectArray.length, (Class<? extends Object[]>) type);
                }

                field.setAccessible(true);
                try {
                    field.set(activity, object);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  public static class SecondActivity extends AppCompatActivity {
        @InjectIntentHelper.InjectIntent(extraName = "extra_string1")
        private String extraString1;
        @InjectIntentHelper.InjectIntent(extraName = "extra_boolean")
        private boolean extraBoolean;
        @InjectIntentHelper.InjectIntent//这边可以省略不写
        private int extraInt;
        @InjectIntentHelper.InjectIntent(extraName = "extra_student_bean")
        private StudentBean extraStudentBean;
        @InjectIntentHelper.InjectIntent(extraName = "extra_student_list")
        private List<StudentBean> extraStudentList;
        @InjectIntentHelper.InjectIntent(extraName = "extra_student_array")
        private StudentBean[] extraStudentArray;

        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            InjectIntentHelper.inject(this);//初始化
        }
    }

三、反射获取泛型的真实类型

相关文章

  • 注解学习笔记

    什么是注解注解分类注解作用分类 元注解 Java内置注解 自定义注解自定义注解实现及使用编译时注解注解处理器注解处...

  • 注解与反射

    注解 声明一个注解类型 元注解 在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之...

  • 1.8 Java 注解annotation

    1.1 注解声明 Java注解Annotation,有声明注解和元注解 元注解:Java提供的元注解,所谓元注解就...

  • 注解的使用

    元注解 注解 注解本质就是接口: 元注解:修饰注解的注解 自定义注解 Text.java FruitName.ja...

  • 注解

    Java注解 注解 元注解 自定义注解 元注解:负责注解其他注解 共有4个标准的meta-annotation类型...

  • Spring高级应用之组合注解和元注解

    1.核心概念: 元注解:可以注解在其他注解上的注解;被注解的注解成为组合注解; 2.组合注解的定义步骤 定义组合注...

  • 2016.10.13-关于注解的自定义和注解的解析

    注解可以分为:1、标识性注解(没有成员变量) 2、注解 3、元注解(注解的注解) 1、注解的自定义 自定义注解的格...

  • 自定义注解

    注解分类 1、代码注解2、编译时注解3、运行时注解 注解范例 使用注解的类 注解解析类 注解实战 需求1、有一张用...

  • 【JAVA】注解

    元注解 用来定义、声明注解的注解。 @Inherited注解 使用此注解声明出来的自定义注解,在使用此自定义注解时...

  • Spring Boot常用注解

    注解速览 配置加载相关 Bean 声明注解 Bean 注入注解 SpringMVC 注解 MyBatis 注解 配...

网友评论

      本文标题:注解

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