Java中通过反射获取带参数类型的方法
Posted akyna-zh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中通过反射获取带参数类型的方法相关的知识,希望对你有一定的参考价值。
date: 2021/8/16
author: jzh
blog: https://akynazh.top
Java中通过反射获取带参数类型的方法
问题的提出
在后端与前端进行数据传送时,需要先把数据进行类型转换,从前端获得的字符串类型(由json类型数据转化而成),转换成一个Java类。通过了解,发现可以采用导入Gson包的方式调用方法实现这个功能:
Person p = new Person(1, "jzh");
Gson gson = new Gson();
String p_json = gson.toJson(p);
Person p1 = gson.fromJson(p_json, Person.class);
从中可见gson.fromJson的第二个参数就是一个类对象,针对于无参的类可以直接通过.class获取类对象,然而有参怎么办呢?如下:
ArrayList<Person> people = new ArrayList<>();
people.add(new Person(1, "jzh"));
people.add(new Person(2, "zh"));
people.add(new Person(3, "z"));
String ps_json = gson.toJson(people);
ArrayList<Person> people1 = gson.fromJson(ps_json,new TypeToken<ArrayList<Person>>() {
}.getType());
这里通过创建匿名内部类的方式实现了转换,也可以通过创建类继承于TypeToken实现。
class MyType extends TypeToken<ArrayList<Person>> {}
ArrayList<Person> people1 = gson.fromJson(ps_json, new MyType().getType());
那么问题来了,这里底层是怎么实现转换的呢?为什么要使用TypeToken?
问题的思考与解决
首先我们通过阅读源码寻找答案。
gson源码中的TypeToken类:
public class TypeToken<T> {
final Class<? super T> rawType;
final Type type;
final int hashCode;
}
constructor:
protected TypeToken() {
this.type = getSuperclassTypeParameter(getClass());
this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
this.hashCode = type.hashCode();
}
这里我们主要研究type成员。
通过方法getSuperclassTypeParameter:
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]);
}
在这里我们就可以找到答案了:
分析可知,步骤如下:
- 从一个Class对象中,获取该对象父类接收到的参数化类型(泛型)
- 判断type类型
- 强制转换为ParameterizedType类型
- 获取parameterized的第一个参数
一. 从一个Class对象中,获取该对象父类接收到的参数化类型(泛型)
Type superclass = subclass.getGenericSuperclass();
其中subclass为:
class 包名.Type.TypeTest.TypeToken<java.util.ArrayList<包名.Type.Person>>
通过前面知道,我们通过继承(或者匿名内部类)的方式创建了一个类,那么我们则可以通过getGenericSuperclass()方法获取这个类的父类,而且是包含了我们需要的类型参数的父类!
二. 判断type类型
if (superclass instanceof Class) {
throw new RuntimeException("Missing type parameter."); // 不是带参数类型
}
Type是一个接口,Class,ParameterizedType等类型都实现了Type,我们需要确保传入的类型参数是一个带参数的类ParameterizedType而不是Class。
三. 强制转换为ParameterizedType类型,即带参数类型,可以调用对应方法获取参数
ParameterizedType parameterized = (ParameterizedType) superclass;
parameterized : 包名.Type.TypeToken<java.util.ArrayList<包名.Type.Person>>
四. 获取parameterized的第一个参数,即java.util.ArrayList<包名.Type.Person>
parameterized.getActualTypeArguments()[0];
源码中的canonicalize方法是实例化一个ParameterizedTypeImpl对象的方法,可以不予理会。
总结
过程粗略一看貌似有些奇怪,但是分析其思路,用通俗的话描述就是,将一个加密的东西放进一个解密的机器里面,在把东西拿出来,你就知道东西具体是什么了!
那么为什么需要一个TypeToken进行包裹呢,个人认为,因为有一个方法getGenericSuperclass可以获得包含了参数类型的父类,用一个类继承于TypeToken, 指定T为自定义的参数类型如ArrayList, 通过调用getGenericSuperclass获取父类,把父类强制转换为ParameterizedType类型,即带参数类型,即可调用对应方法获取参数。
date: 2021/8/16
author: jzh
blog: https://akynazh.top
以上是关于Java中通过反射获取带参数类型的方法的主要内容,如果未能解决你的问题,请参考以下文章
JAVA反射问题,一个方法类的参数能否通过设置成泛型或者啥来接收反射的CLASS。
Java反射机制获取set()方法 并且为set()方法传值