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还有四个对应的子接口
四个子接口可以让我们获得泛型的真实类型
- TypeVariable:泛型类型变量。可以泛型上下限等信息;
- ParameterizedType:具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)
- GenericArrayType:当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。
- 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());
}