美文网首页
Java注解Annotation

Java注解Annotation

作者: TangBuzhi | 来源:发表于2018-04-19 16:05 被阅读0次

    本文将围绕下面三个问题展开:

    1.Annotation是什么?
    2.有什么作用?
    3.学习理解它有什么好处?

    1.Annotation是什么?

    从JDK5开始,java增加了对元数据(MetaData)的支持,通过Annotation(注解)来实现。
    它可标注源码,在编译期或运行时被注解处理器处理,然而这些标注并不会对源码产生任何直接的影响。
    备注:元数据是描述数据的数据。

    分类
    • 一般分类:基本注解,元注解,自定义注解;
    • 注解保留时间:源码期注解,编译期注解,运行时注解;
      下面根据一般分类进行阐述
    1.1基本注解

    基本注解均位于java.lang包

    • @Override:限定重写父类方法(仅修饰方法)
      指定方法重载,强制一个子类必须覆盖父类的方法。通过该注解,编译器会在编译时进行严格的检查,避免子类中出现重写方法的方法名错误等问题。
    • @Deprecated:表示已过时
      表示某个程序元素已过时,当其他程序使用这些元素时,编译器会给出警告。
    • @SuppressWarnings:抑制编译器警告
      指示该Annotation修饰的元素取消显示指定的编译器警告,注解会从被注释的类一直作用到类里的方法和成员变量等。要在注解的括号内为value赋值,value的值为需要关闭的编译器警告。
    • @SafeVarargs:抑制堆污染警告
      Java7引入,发生堆污染的代码使用此注解后,编译时程序不会发出任何警告。
    • @FunctionInterface:函数式接口(仅修饰接口)
      Java8引入,用来指定某个接口必须是函数式接口,通知编译器进行更严格的检查。
    1.2元注解

    备注:元注解是和元数据一样,是修饰注解的注解。
    java提供的元注解(Meta Annotation),均位于java.lang.annotation包,可修饰其他注解。

    • @Target
      指定被修饰的注解能修饰哪些程序,仅修饰注解。它包含值为value(ElementType类型)的成员变量,使用它必须为该value成员变量指定值,取值如下:
      1.ElementType.ANNOTATION_TYPE:仅修饰Annotation
      2.ElementType.TYPE:接口、类、枚举、注解
      3.ElementType.FIELD:成员变量(字段、枚举的常量)
      4.ElementType.METHOD:方法
      5.ElementType.PARAMETER:方法参数
      6.ElementType.CONSTRUCTOR:构造函数
      7.ElementType.LOCAL_VARIABLE:局部变量
      8.ElementType.PACKAGE:
      9.ElementType.TYPE_PARAMETER:java8引入
      10.ElementType.TYPE_USE:java8引入
    • @Retention
      指定被修饰的注解可以保留多久,仅修饰注解。它包含值为value(RetentionPolicy类型)的成员变量,使用它必须为该value成员变量指定值,取值如下:
      1.RetentionPolicy.SOURCE
      注解只保留在源代码中(.java文件中),编译器直接丢弃该注解。
      2.RetentionPolicy.CLASS
      编译器将把注解记录在.class文件中。当运行java程序时,JVM不可获取注解信息。
      3.RetentionPolicy.RUNTIME
      编译器将把注解记录在.class文件中。当运行java程序时,JVM也可获取注解信息,程序可以通过反射获取该注解信息。
    • @Documented
      指定被修饰的注解将被javadoc工具提取成文档,即该注解将被包含在javadoc中。
    • @Inherited
      指定被修饰的注解具有继承性,即子类可以继承父类中的该注解。
    • @Repeatable
      java8引入,用于定义重复注解
    1.3自定义注解

    自定义注解要使用@interface关键字。定义注解是可以带或不带成员变量,成员变量以抽象方法来声明,方法名可自定义,返回值可以通过default为成员变量指定默认值。设定了默认值的成员变量可以在使用时不指定值,没有设置默认值的成员变量必须在使用注解时传入值。
    自定义注解格式:

    public @interface xxx {
        返回值类型 方法名() default 默认值;
    }
    举例:
    public @interface MyTag {
        int value() default 0;
    }
    

    根据Annotation中是否包含成员变量,Annotation可分为两类:

    • 标记注解
      不包含成员变量的注解,利用自身的存在与否来提供信息,如@Override
    • 元数据注解
      包含成员变量的注解,为程序提供额外的元数据
      备注:注解不提供任何业务逻辑处理,仅提供元数据或标记,如需处理这些标记或元数据,还要自行提取注解信息。

    2.Annotation作用

    通过问题1,我们知道,注解可以为我们的程序提供标记或额外的数据,以便我们处理相关业务逻辑。
    下面举个简单例子说明一下。
    如统计学生信息,这里就简单例举学生信息有姓名,年龄,性别和住址,其他略,因为有大部分学生信息基本相同,如果每录入都写一次,会很麻烦,这里使用注解即可完美解决问题。

    2.1首先定义一个学生信息的注解,如下:
    @Target({ElementType.FIELD, ElementType.METHOD})//这里定义该注解适用于方法和成员变量
    @Retention(RetentionPolicy.RUNTIME)//这里定义该注解在保留到运行时
    public @interface StudentInfo {//学生信息注解StudentInfo 
        enum Gender {MALE, FEMAIL}//性别枚举
    
        String name();//姓名
    
        int age();//年龄
        //把大部分相同的信息设置一个默认值,该默认值即为多数相同的信息
        Gender gender() default Gender.MALE;//默认性别男
    
        String address() default "hangzhou";//默认地址杭州
    }
    
    2.2把上面定义的注解应用于逻辑中
    public class Student {
        @StudentInfo(name = "xiaomin", age = 10, gender = StudentInfo.Gender.FEMAIL)
        private String xiaominField;//这里的String可以是任意类型,因为我们用不着
        @StudentInfo(name = "zhangsan", age = 12, address = "beijing")
        public String zhangsanField;
        @StudentInfo(name = "lisi", age = 14)
        private String lisiField;
        @StudentInfo(name = "wangwu", age = 16, gender = StudentInfo.Gender.FEMAIL, address = "shanghai")
        private String wangwuField;
    
        @StudentInfo(name = "xiaomin", age = 8, gender = StudentInfo.Gender.FEMAIL)
        private void xiaominMethod() {
        }
    
        @StudentInfo(name = "zhangsan", age = 15, address = "beijing")
        public void zhangsanMethod() {
        }
    
        public void lisiMethod() {
        }
    
        @StudentInfo(name = "wangwu", age = 10, gender = StudentInfo.Gender.FEMAIL, address = "shanghai")
        public void wangwuMethod() {
        }
    }
    
    2.3对应用注解的逻辑代码进行处理,获得最终信息
    public class StudentActivity extends AppCompatActivity {
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_student);
            Student student = new Student();
            processField(student);
            processMethod(student);
        }
    
        private void processField(Student student) {
            Class<?> clz = student.getClass();
            Field[] declaredFields = clz.getDeclaredFields();
            for (Field field : declaredFields) {
                StudentInfo annotation = field.getAnnotation(StudentInfo.class);
                if (annotation != null) {
                    String name = annotation.name();
                    int age = annotation.age();
                    StudentInfo.Gender gender = annotation.gender();
                    String address = annotation.address();
                    System.out.println(field.getName() + " ==> " + name + " ==> " + age + " ==> " + gender.toString() + " ==> " + address);
                }
            }
        }
    
        private void processMethod(Student student) {
            Class<?> clz = student.getClass();
            Method[] declaredMethods = clz.getDeclaredMethods();
            for (Method method : declaredMethods) {
                StudentInfo annotation = method.getAnnotation(StudentInfo.class);
                if (annotation != null) {
                    String name = annotation.name();
                    int age = annotation.age();
                    StudentInfo.Gender gender = annotation.gender();
                    String address = annotation.address();
                    System.out.println(method.getName() + " ==> " + name + " ==> " + age + " ==> " + gender.toString() + " ==> " + address);
                }
            }
        }
    }
    

    获得信息(打印栏)如下:

    System.out: lisiField ==> lisi ==> 14 ==> MALE ==> hangzhou
    System.out: wangwuField ==> wangwu ==> 16 ==> FEMAIL ==> shanghai
    System.out: xiaominField ==> xiaomin ==> 10 ==> FEMAIL ==> hangzhou
    System.out: zhangsanField ==> zhangsan ==> 12 ==> MALE ==> beijing
    
    System.out: wangwuMethod ==> wangwu ==> 10 ==> FEMAIL ==> shanghai
    System.out: zhangsanMethod ==> zhangsan ==> 15 ==> MALE ==> beijing
    System.out: xiaominMethod ==> xiaomin ==> 8 ==> FEMAIL ==> hangzhou
    

    3.理解Annotation的好处

    纵观Android第三方lib,比如Butterknife,EventBus,Retrofit,Dagger等,里面均有使用注解。如果我们理解了注解,那么阅读这些优秀第三方库,也是极有帮助的。
    备注:阅读源码是一件痛并快乐的事情...
    下面我就再举个例子,仿Butterknife写个自定义的View注入工具。

    3.1首先定义几个自定义的注解:
    //setContentView
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ContentView {
        @LayoutRes int value();
    }
    //findViewById
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface BindView {
        @IdRes int value();
    }
    //setOnClickListener
    @Documented
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ClickView {
        int[] value();
    }
    
    3.2编写关于上面几个注解的逻辑代码(工具类)
        public static void bind(Object obj) {
            contentView(obj);
            bindView(obj);
            clickView(obj);
        }
    
        private static void contentView(Object obj) {
            Class<?> clz = obj.getClass();
            ContentView contentView = clz.getAnnotation(ContentView.class);
            if (contentView != null) {
                int contentLayoutResId = contentView.value();
                try {
                    Method setContentView = clz.getMethod("setContentView", new Class[]{int.class});
                    setContentView.invoke(obj, contentLayoutResId);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        private static void bindView(Object obj) {
            Class<?> clz = obj.getClass();
            Field[] fields = clz.getDeclaredFields();
            for (Field field : fields) {
                BindView bindView = field.getAnnotation(BindView.class);
                if (bindView != null) {
                    int viewId = bindView.value();
                    if (obj instanceof Activity) {
                        Activity activity = (Activity) obj;
                        View view = activity.findViewById(viewId);
                        field.setAccessible(true);
                        try {
                            field.set(obj, view);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    
        private static void clickView(final Object obj) {
            Class<?> clz = obj.getClass();
            Method[] declaredMethods = clz.getDeclaredMethods();
            for (final Method declaredMethod : declaredMethods) {
                ClickView clickView = declaredMethod.getAnnotation(ClickView.class);
                if (clickView != null) {
                    declaredMethod.setAccessible(true);
                    Object clickListener = Proxy.newProxyInstance(View.OnClickListener.class.getClassLoader(), new Class[]{View.OnClickListener.class}, new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            return declaredMethod.invoke(obj, args);
                        }
                    });
    
                    int[] viewIds = clickView.value();
                    for (int viewId : viewIds) {
                        if (obj instanceof Activity) {
                            Activity activity = (Activity) obj;
                            View view = activity.findViewById(viewId);
                            try {
                                Method setOnClickListener = view.getClass().getMethod("setOnClickListener", new Class[]{View.OnClickListener.class});
                                setOnClickListener.invoke(view, clickListener);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        if (obj instanceof Fragment) {
    
                        }
                    }
                }
            }
        }
    
    3.3使用
    #activity_main.xml布局#
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:orientation="vertical">
    
        <TextView
            android:id="@+id/txt"
            android:layout_width="match_parent"
            android:layout_height="150dp"
            android:background="@android:color/white"
            android:gravity="center"
            android:text="123"/>
    
        <Button
            android:id="@+id/btn_set"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:text="设置TextView值"
            android:textAllCaps="false"/>
    
        <Button
            android:id="@+id/btn_back"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:text="初始TextView值"
            android:textAllCaps="false"/>
    </LinearLayout>
    
    #MainActivity.java#
    @ContentView(R.layout.activity_main)
    public class MainActivity extends AppCompatActivity {
    
        @BindView(R.id.txt)
        TextView mTxt;
        @BindView(R.id.btn_set)
        Button mBtnSet;
        @BindView(R.id.btn_back)
        Button mBtnBack;
        
        private String initTxt = "我是初始TextView值";
        private String lastTxt = "TextView之后设置的值";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            TangBind.bind(this);
    
            mTxt.setText(initTxt);
            /*mBtnSet.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mTxt.setText(lastTxt);
                }
            });
            mBtnBack.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mTxt.setText(initTxt);
                }
            });*/
        }
    
        @ClickView({R.id.btn_set, R.id.btn_back})
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.btn_set:
                    mTxt.setText(lastTxt);
                    break;
                case R.id.btn_back:
                    mTxt.setText(initTxt);
                    break;
                default:
                    break;
            }
        }
    }
    

    效果图如下:

    bindview.gif
    代码托管地址:AnnotationDemo

    相关文章

      网友评论

          本文标题:Java注解Annotation

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