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 通过反射怎么获取方法中参数值

JAVA反射问题,一个方法类的参数能否通过设置成泛型或者啥来接收反射的CLASS。

Java反射机制获取set()方法 并且为set()方法传值

C#反射执行方法返回List,怎么获取List

JAVA反射中通过Class.forname()如何带参数的方法怎么赋值和调用呀?

java 反射技巧