【08】保留在运行时级别注解的应用场景

(1)一个人只要自己不放弃自己,整个世界也不会放弃你.
(2)天生我才必有大用
(3)不能忍受学习之苦就一定要忍受生活之苦,这是多么痛苦而深刻的领悟.
(4)做难事必有所得
(5)精神乃真正的刀锋
(6)战胜对手有两次,第一次在内心中.
(7)编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~

保留在运行时级别注解的应用场景

1.注解的应用场景

(1)根据注解的保留级别不同,对注解的使用自然存在不同场景。

(2)由注解的三个不同保留级别可知,注解作用于:源码、字节码与运行时时你能举一些案例吗?

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

2.保留在RUNTIME级别的注解结合反射技术的应用

(1)在程序运行期间,通过反射技术动态获取注解及其元素,从而完成不同的逻辑判定。

2.1反射

在这里插入图片描述

在这里插入图片描述

(1)一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的,并且能够获得此类的引用。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。

(2)反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用 JDK 提供的反射 API 进行反射调用。

(3)反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。是Java被视为动态语言的关键。

2.2Java反射机制主要提供了以下功能

  • 在运行时构造任意一个类的对象
  • 在运行时获取或者修改任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法(属性)

2.3注解+反射自动完成findViewById

2.3.1Field与DecleredField的区别

(1)Field

获得自己+父类的成员(不包括private,只能是public)

(2)DecleredField

只能获得自己的成员(不能获得父类,所有作用域)

2.3.1定义注解

package com.gdc.javabase.annotation;

import android.annotation.SuppressLint;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import androidx.annotation.IdRes;

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

2.3.2 使用注解与反射实现findViewById

public class InjectUtils {
    
    
    //s1.反射是基于CLASS
    public static void injectView(Activity activity) {
    
    
        Class<? extends Activity> cls = activity.getClass();

        //s1.获得此类所有的成员变量
        Field[] fields = cls.getDeclaredFields();

        //s2.遍历
        for(Field field : fields){
    
    
            //s3.判断属性是否被InjetView注解声明
            boolean ret = field.isAnnotationPresent(InjectView.class);
            if(ret){
    
    
                //s4.判断这个属性上有注解了,此时就还需要获取这个注解
                InjectView injectView = field.getAnnotation(InjectView.class);
                //s5.获取注解中设置的id值
                int id = injectView.value();

                View view = activity.findViewById(id);
                //s6.反射设置属性的值
                //s7.设置访问权限,允许操作私有成员private属性,
                field.setAccessible(true);

                //s8.设置(反射赋值)
                try {
    
    
                	//s9.通过反射将注解中的值设置给view
                    field.set(activity,view);
                } catch (IllegalAccessException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
    }
}

2.3.3使用

package com.gdc.javabase.activity;

import android.os.Bundle;
import android.widget.TextView;

import com.gdc.javabase.R;
import com.gdc.javabase.annotation.InjectView;
import com.gdc.javabase.annotation.Lance;
import com.gdc.javabase.inject.InjectUtils;

import androidx.appcompat.app.AppCompatActivity;


@Lance("注解作用于类上")
public class MainActivity extends AppCompatActivity {
    
    

    private int i;

    private int j;

    //s1.利用反射与注解完成findViewById的功能
    @InjectView(R.id.tv)
    private TextView tv;

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

        InjectUtils.injectView(this);

        tv.setText("控件初始化成功!");
    }
}

3.反射获取泛型的真实类型

3.1Type

(1)当我们对一个泛型类进行反射时,需要得到泛型中的真实数据类型,来完成如json反序列化的操作。此时需要通过Type 体系来完成。Type 接口包含了一个实现类(Class)和四个实现接口,他们分别是:

  • TypeVariable
    泛型类型变量。可以泛型上下限定信息;

  • ParameterizedType
    具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)

  • GenericArrayType
    当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。

  • WildcardType
    通配符泛型,获得上下限信息;

(2)Type体系是什么?

  • 是java.lang.refect包下面是一个接口,就一个方法getTypeName;有接口就有实现类。
  • Type的实现类就是Class,平时用的Class就是Type接口的实现类。
  • 它还有四个对应的子接口,如上,这些子接口可以让我们获得泛型真实的类型。

4.9.1TypeVariable

(1)泛型类型变量。可以获取泛型上下限定信息;

package com.annotations.class1.test;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

public class TestType <K extends Comparable & Serializable,V> {
    
    
    K key;
    V value;

    public static void main(String[] args) throws NoSuchFieldException {
    
    
        //s1.获取字段类型
        Field fk = TestType.class.getDeclaredField("key");
        Field fv = TestType.class.getDeclaredField("value");

        //s2.获取泛型类型
        TypeVariable keyType = (TypeVariable) fk.getGenericType();
        TypeVariable valueType = (TypeVariable) fv.getGenericType();

        //s3.getName打印变量名
        System.out.println(keyType.getName());
        System.out.println(valueType.getName());

        //s4.getGenericDeclaration获取泛型声明
        System.out.println(keyType.getGenericDeclaration());
        System.out.println(valueType.getGenericDeclaration());

        //s5.getBounds()方法
        System.out.println("K的上界:");
        for(Type type : keyType.getBounds()){
    
    
            System.out.println(type);
        }

        System.out.println("V的上界:");
        for(Type type : valueType.getBounds()){
    
    
            System.out.println(type);
        }
    }
}

运行结果:

K
V
class com.annotations.class1.test.TestType
class com.annotations.class1.test.TestType
K的上界:
interface java.lang.Comparable
interface java.io.Serializable
V的上界:
class java.lang.Object

4.9.2ParameterizedType

(1)具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)

(2)案例

package com.annotations.class1.test;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;

public class TestType1 {
    
    
    Map<String,String> map;

    public static void main(String[] args) throws Exception {
    
    
        //s1.获取指定字段
        Field field = TestType1.class.getDeclaredField("map");
        //s2.输出字段的泛型类型
        System.out.println(field.getGenericType());
        //s3.获取字段的泛型类型
        ParameterizedType pType = (ParameterizedType) field.getGenericType();
        //s4.获取泛型类型的实际类型
        System.out.println(pType.getRawType());
        //s5.获取字段的泛型类型的参数类型
        for(Type type : pType.getActualTypeArguments()){
    
    
            System.out.println(type);
        }
    }
}

(2)运行结果

java.util.Map<java.lang.String, java.lang.String>
interface java.util.Map
class java.lang.String
class java.lang.String

4.9.3GenericArrayType

(1)当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。

(2)案例

package com.annotations.class1.test;

import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.util.List;

public class TestType2<T> {
    
    

    List<String>[] lists;

    public static void main(String[] args) throws NoSuchFieldException {
    
    
        //s1.获取字段
        Field f = TestType2.class.getDeclaredField("lists");
        //s2.获取泛型数组类型
        GenericArrayType genericArrayType = (GenericArrayType) f.getGenericType();
        //s3.获取泛型数组元素类型
        System.out.println(genericArrayType.getGenericComponentType());
    }
}

(2)运行结果

java.util.List<java.lang.String>

4.9.4WildcardType

(1)通配符泛型,获得上下限信息;

(2)案例代码

package com.annotations.class1.test;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.WildcardType;
import java.util.List;

public class TestType3 {
    
    
    //s1.泛型上界限定
    private List<? extends Number> a;

    //s2.泛型下界限定
    private List<? super Number> b;

    public static void main(String[] args) throws NoSuchFieldException {
    
    
        //s1.得到字段
        Field fieldA = TestType3.class.getDeclaredField("a");
        Field fieldB = TestType3.class.getDeclaredField("b");

        //s2.获取泛型类型
        ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();
        ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();

        //s3.从泛型中拿到通配符类型
        WildcardType wTypeA = (WildcardType) pTypeA.getActualTypeArguments()[0];
        WildcardType wTypeB = (WildcardType) pTypeB.getActualTypeArguments()[0];

        //s4.测试方法
        System.out.println(wTypeA.getUpperBounds()[0]);
        System.out.println(wTypeB.getLowerBounds()[0]);

        //s5.查看通配符类型到底是什么
        System.out.println(wTypeA);

    }
}

(2)运行结果

class java.lang.Number
class java.lang.Number
? extends java.lang.Number

4.9.5泛型与注解的配合使用

4.9.5.1案例场景描述

服务器给我们响应数据,返回一个json数据,我们对这个json数据进行一个JavaBean的封装,它有可能这样一个数据结构,这个数组一会儿可能是A类型,或者其他接口又变成B类型的,再换一个接口又变成C类型的,所以我们在定义一个实体的时候,会用泛型来定义。

4.9.5.2用Gosn进行序列化服务器响应的数据
package com.gdc.javabase.refflect.type.gson;

import com.google.gson.Gson;

public class Deserialize1 {
    
    

    /**
     * 定义泛型
     * @param <T>
     */
    static class Response<T>{
    
    
        //s1.数据一会儿可能是A类型的,一会儿也可能是B类型的,所以来定义这种类型实体的时候采用了泛型定义。
        T data;

        //s2.成功码
        int code;

        //s3.描述
        String message;

        @Override
        public String toString() {
    
    
            return "Response{" + "data=" + data + ", code=" + code + ", message='" + message + '\'' + '}';
        }

        public Response(T data, int code, String message) {
    
    
            this.data = data;
            this.code = code;
            this.message = message;
        }
    }

    //响应实体类数据
    static class Data{
    
    

        String result;

        public Data(String result) {
    
    
            this.result = result;
        }

        @Override
        public String toString() {
    
    
            return "Data{" + "result='" + result + '\'' + '}';
        }
    }

    public static void main(String[] args) {
    
    
        //s1.使用泛型
        Response<Data> dataResponse =
                new Response(new Data("数据"),1,"成功");

        //s2.用Gosn进行序列化服务器响应的数据
        Gson gson = new Gson();
        String json = gson.toJson(dataResponse);
        System.out.println(json);
    }
}

4.9.5.3用Gosn反序列化服务器响应的数据
public class Deserialize2 {
    
    
    /**
     * 定义泛型
     * @param <T>
     */
    static class Response<T>{
    
    
        //s1.数据一会儿可能是A类型的,一会儿也可能是B类型的,所以来定义这种类型实体的时候采用了泛型定义。
        T data;

        //s2.成功码
        int code;

        //s3.描述
        String message;

        @Override
        public String toString() {
    
    
            return "Response{" + "data=" + data + ", code=" + code + ", message='" + message + '\'' + '}';
        }

        public Response(T data, int code, String message) {
    
    
            this.data = data;
            this.code = code;
            this.message = message;
        }
    }

    //响应实体类数据
    static class Data{
    
    

        String result;

        public Data(String result) {
    
    
            this.result = result;
        }

        @Override
        public String toString() {
    
    
            return "Data{" + "result='" + result + '\'' + '}';
        }
    }

    public static void main(String[] args) {
    
    
        //s1.使用泛型
        Response<Data> dataResponse =
                new Response(new Data("数据"),1,"成功");

        //s2.用Gosn进行序列化服务器响应的数据
        Gson gson = new Gson();
        String json = gson.toJson(dataResponse);
        System.out.println(json);

        //s3.反序列化json数据
        /*
        s3.1为什么不能按照如下代码反序列化?
        (1)如果不是一个泛型类型的话,我们就直接传Response.class就可以了。
        (2)但是此时的Response是一个泛型类型。
        (3)为什么会报ClassCastException,因为我们指定Response<Data>泛型的实际类型参数为Data,
        而gson.fromJson(json,Response.class)返回的是一个LinkedTreeMap类型。
        (4)在反序列化的时候,Gson不知道有Data的存在,只告诉了它给我反序列化出来一个Response,所以这个时候它会把
         Data认为是LinkedTreeMap,所以如何使用强制的将LinkedTreeMap转换为Data这个实际类型参数的话,就会报类型
         转换异常。
         */
        /*Response<Data> response = gson.fromJson(json, Response.class);
        System.out.println(response.data.getClass());*/

        /*
            s3.2应该使用Type类型解决这个问题
            (1)Gson中提供了一个TypeToken泛型类型
            (2)TypeToken<Response<Data>>泛型中有我们需要的Response<Data>泛型信息,所以能帮我们反序列化成功
            (3)为什么泛型擦除之后,仍然可以得到泛型的真实类型?
            因为TypeToken<Response<Data>>(){}其实是一个匿名内部类,它在CLASS中会记录Response<Data>泛型信息,
            这部分信息不会被擦除。
         */
        Type type = new TypeToken<Response<Data>>(){
    
    }.getType();
        System.out.println(type);
        Response<Data> response = gson.fromJson(json, type);
        System.out.println(response.data.getClass());
    }
}
4.9.5.4自定义泛型类型实现反序列化

public class Deserialize {
    
    

    static class Response<T>{
    
    
        //s1.数据一会儿可能是A类型的,一会儿也可能是B类型的,所以来定义这种类型实体的时候采用了泛型定义。
        T data;

        //s2.成功码
        int code;

        //s3.描述
        String message;

        @Override
        public String toString() {
    
    
            return "Response{" + "data=" + data + ", code=" + code + ", message='" + message + '\'' + '}';
        }

        public Response(T data, int code, String message) {
    
    
            this.data = data;
            this.code = code;
            this.message = message;
        }
    }

    //响应实体类数据
    static class Data{
    
    

        String result;

        public Data(String result) {
    
    
            this.result = result;
        }

        @Override
        public String toString() {
    
    
            return "Data{" + "result='" + result + '\'' + '}';
        }
    }


    /**
     * 不使用gson中的TypeToken,自定义一个TypeReference<T>泛型
     * @param <T>
     */
    static class TypeReference<T>{
    
    

        Type type;

        public TypeReference(){
    
    
            //s1.在运行的时候来获得这个泛型T类型
            Type genericSuperclass = getClass().getGenericSuperclass();
            //s2.Type genericSuperclass得到的是一个ParameterizedType类型,可以强转
            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
            //s3.因为该类可以指定多个泛型,不仅仅是T. TypeReference<T,A,B,C>所以是个数组
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            //s4.拿到T
            type = actualTypeArguments[0];
        }

        public Type getType() {
    
    
            return type;
        }
    }

    public static void main(String[] args) {
    
    
        //s1.使用泛型
        Response<Data> dataResponse = new Response(new Data("数据"),1,"成功");

        //s2.用Gosn进行序列化服务器响应的数据
        Gson gson = new Gson();
        String json = gson.toJson(dataResponse);
        System.out.println(json);

        //s3.反序列化json数据

        /*
        (1)注意加{}与不加的区别,不加的时候会报强制转换异常,有{}代表的是匿名内部类,没有{}就是一个对象

        (2)对象的类型是TypeReference,然后这个TypeReference的class文件中不会记录Response<Data>,
         这是因为泛型擦除引起的。

        (3)如果是匿名内部类,它就会在TypeReference的class字节码文件中记录Response<Data>,所以不会导致反序列化失败。

        (4)Type type = new TypeReference<Response<Data>>().getType();

        */
        Type type = new TypeReference<Response<Data>>(){
    
    }.getType();

        System.out.println(type);

        //s5.反序列化响应数据
        /*
        (1)因为new TypeReference<Response<Data>>(){}.getType()得到的Type有泛型Response<Data>信息
        所以可以反序列化成功。
         */
        Response<Data> response = gson.fromJson(json,type);
        System.out.println(response.data.getClass());

    }

}

4.9.5.4.1注意
new TypeReference<Response<Data>>().getType();
new TypeReference<Response<Data>>(){
    
    }.getType();1)加{
    
    }与不加的含义不一样,一个是匿名内部类,一个是创建的一个对象。

(2)匿名内部类的信息在类的Class信息中会被保留下来,即TypeReference<Response<Data>>泛型的泛型信息可以被保留下来,可以通过ASMBytecodeViewer插件查看字节码信息,明确了这一点,也就明确了为什么需要加上{
    
    }来确保反序列化成功。

(3)没{
    
    }是创建一个对象,由于泛型擦除的原因,将不清楚TypeReference<Response<Data>>这个泛型里面究竟放的是什么数据类型,所以反序列化会失败。

5.总结

(1)重点在于注解的应用场景

(2)把注解定义在CLASS级别,可以解析字节码文件,解析的时候来获取注解中的各种信息,可以利用注解对某些方法进行修改。

(3)如何来区分某些方法呢?这其实就是一个AOP编程思想,而向切面编程。

(4)要对CLASS文件中的某些方法实现注入,要对方法区分就需要给要注入的方法打上注解,用不同的注解加以区分,每一个注解所标示的方法就相当于是一个切面。

(5)把注解定义在RUNTIME运行时级别,可以在运行时用反射去获取到注解的一些信息。

猜你喜欢

转载自blog.csdn.net/xiogjie_67/article/details/108629015