反射与Type体系

1.反射

一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的,并且能够获得此类的引用。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用 JDK 提供的反射 API 进行反射调用。反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。 是Java被视为动态语言的关键。
反射都是基于class的

2.例子

Butterknife 早期实现 注解+反射 自动完成findViewById

@Retention(RetentionPolicy.RUNTIME)  //运行时 去反射实现它
@Target(ElementType.FIELD)
public @interface InjectView {
    
    
    @IdRes int value();
}
public class InjectUtils {
    
    
    public static void injectView(Activity activity){
    
    

        //通过对象得到class
        Class<? extends Activity> cls = activity.getClass();

        //获得此类所有的成员
        Field[] declaredFields = cls.getDeclaredFields();
        for (Field filed : declaredFields) {
    
    //上百个属性 迭代次数很多 影响效率
            //判断属性是否被InjectView注解声明
            if(filed.isAnnotationPresent(InjectView.class)){
    
    
                InjectView injectView = filed.getAnnotation(InjectView.class);
                //获取注解中设置的id
                int id = injectView.value();
                View view = activity.findViewById(id);
                //反射设置属性的值 如果是private 不能直接修改
                filed.setAccessible(true);//设置访问权限,允许操作private的属性
                try {
    
    
                    //赋值 怎么做到把view赋值给mainActivity里面的 反射赋值
                    filed.set(activity,view);
                } catch (IllegalAccessException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
    }
}

cls.getField()
获得自己+父类的成员(不包括private,只能是public)
cls.getDeclaredField()
只能获得自己的成员(不包括父类,所有作用域)
怎么获得父类的私有?
cls.getSuperclass().getDeclaredField()

public class MainActivity extends AppCompatActivity {
    
    
    int i;
    int j;

    @InjectView(R.id.tv)
    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InjectUtils.injectView(this);
        tv.setText("puppy love");
    }
}

3.反射结合泛型Type体系

Java.lang.reflect.Type 接口

public interface Type {
    
    
    default String getTypeName() {
    
    
        return this.toString();
    }
}

Type的实现类是Class

public final class Class<T> implements Serializable, GenericDeclaration, Type, AnnotatedElement {
    
    ...}

Type还有四个对应的子接口
在这里插入图片描述
四个子接口可以让我们获得泛型的真实类型

  1. TypeVariable:泛型类型变量。可以泛型上下限等信息;
  2. ParameterizedType:具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)
  3. GenericArrayType:当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。
  4. WildcardType:通配符泛型,获得上下限信息;

响应服务器数据例子:

public class Deserialize {
    
    

    static class Response<T> {
    
    
        T data;
        int code;
        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 +
                    '}';
        }
    }
 }

指定T为Data类型,实际上Gson在反序列化的时候不知道有Data的存在,只知道要反序列化一个Response,会把Data认为是LinkedTreeMap,所以用Response 接收的话就会报错 类型转换异常ClassCastException。

public static void main(String[] args) {
    
    
        Response<Data> dataResponse = new Response(new Data("数据"), 123, "ok");

        Gson gson = new Gson();
        String json = gson.toJson(dataResponse);
        System.out.println(json);

        //反序列化......
        Response<Data> response = gson.fromJson(json, Response.class);

        //指定T为Data类型,实际上Gson在反序列化的时候不知道有Data的存在,
        //只知道要反序列化一个Response,会把Data认为是LinkedTreeMap
        //所以用Response<Data> 接收的话就会报错 类型转换异常ClassCastException
        System.out.println(response.data.getClass());//报错
    }
Exception in thread "main" java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.example.test.reflect.Deserialize$Data
	at com.example.test.reflect.Deserialize.main(Deserialize.java:60)

所以反序列化时应该用Type

public static void main(String[] args) {
    
    
        Response<Data> dataResponse = new Response(new Data("数据"), 123, "ok");

        Gson gson = new Gson();
        String json = gson.toJson(dataResponse);
        System.out.println(json);

        //反序列化......
        Type type = new TypeToken<Response<Data>>() {
    
    
        }.getType();
        System.out.println(type);

        Response<Data> response = gson.fromJson(json, type);

        System.out.println(response.data.getClass());
    }

输出type中有Response和Data的信息,所以在Gson反序列化的时候就能够帮助我们反序列化成功。
response.data.getClass()的类型就是我们想要得到的Data实体

{"data":{"result":"数据"},"code":123,"message":"ok"}
com.example.test.reflect.Deserialize$Response<com.example.test.reflect.Deserialize$Data>
class com.example.test.reflect.Deserialize$Data

type怎么做到有这些信息的?

protected TypeToken() {
    
    
    this.type = getSuperclassTypeParameter(getClass());
    this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
    this.hashCode = type.hashCode();
  }
static Type getSuperclassTypeParameter(Class<?> subclass) {
    
    
    Type superclass = subclass.getGenericSuperclass();
    if (superclass instanceof Class) {
    
    
      throw new RuntimeException("Missing type parameter.");
    }
    ParameterizedType parameterized = (ParameterizedType) superclass;
    return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
  }

Type构造 如果superclass是泛型类型的话,那么会被装载为ParameterizedType ,这个子接口可以让我们得到具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)。

不用Gson里面定义的TypeToken,自己写TypeRefrece:

public class TypeRefrence <T> {
    
    
    Type type;
    T t;

    protected TypeRefrence() {
    
    
        //获得泛型类型
        Type genericSuperclass = getClass().getGenericSuperclass();
        ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
        //因为类泛型可以定义多个  A<T,E..> 所以是个数组
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        type = actualTypeArguments[0];
    }

    public Type getType() {
    
    
        return type;
    }
}
public static void main(String[] args) {
    
    
        Response<Data> dataResponse = new Response(new Data("数据"), 123, "ok");

        Gson gson = new Gson();
        String json = gson.toJson(dataResponse);
        System.out.println(json);

        //反序列化......
        //Type type = new TypeToken<Response<Data>>() {
    
    
        //}.getType();

        //没有花括号 不能成功得到
        //Type type = new TypeRefrence<Response<Data>>().getType();


        //有花括号就可以
        //这就是为什么TypeToken的构造方法要写成受保护的
        //如果TypeRefrence放在不同的包下面 构造方法又是受保护的就必须加{}
        /**
         * 有{}代表是匿名内部类
         * 没有{}就是对象,对象的类型是TypeRefrence它不知道Response<Data>,因为T被擦除为Object
         * 加了{} 就相当于查看了TypeRefrence的子类 里面就有了Response<Data>
         *     static class ChildTypeRefrence{
         *         Response<Data>  t;
         *     }
         * 有花括号: 代表是匿名内部类,创建一个匿名内部类的实例对象
         * 没花括号:创建实例对象
         * 为什么加上{}变成匿名内部类就能拿到信息?
         * 这个类是动态创建的类,里面记录了签名信息,签名信息记录了泛型类型
         * javac 编译时创建的
         * 内名内部类增加了一个class 一个类 而不是一个对象
         * 内部类里面确认了里面有一个Response<Data>这种类型,他就会保留这个签名,把Response<Data>类型信息保留下来
         */
        Type type = new TypeRefrence<Response<Data>>(){
    
    }.getType();

        System.out.println(type);

        Response<Data> response = gson.fromJson(json, type);

        System.out.println(response.data.getClass());
    }

猜你喜欢

转载自blog.csdn.net/qq_40270270/article/details/112986306