Java-如何获取泛型类型
场景
假设我们定义了一个类,内部有一个数据结构,泛型为 T,当我们输入一个 Json 字符串,想把这个 Json 反序列化成对象,那么此时,我们就需要知道这个泛型的类型。
具体代码场景如下:
抽象父类(包含泛型):
1 | abstract class Base<T extends Comparable<T>> { |
我们想在该类中输入 Json,并将字符串反序列化成对象。比如下面这样:
1 | /** |
上面的例子中,子类定义了泛型,但获取泛型类型是在父类。
所以,重点在 deSerializable() 方法的实现,我们需要一个 Class
如何实现?
先说结论:通过 Java 反射包的 ParameterizedType 工具获得泛型具体类型。
例如:下面的代码:
1 | public static void main(String[] args) { |
首先我们将一个对象序列化成 Json 字符串,模拟外部输入。然后呢?创建一个子类对象,得到这个 Son 的 Class 。
关键地方来了,调用 getGenericSuperclass 方法,这个方法的作用是:返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type。
所以这里会得到一个 ParameterizedTypeImpl 类型的对象。注意:这个类是 Sun 包下的,不是开源的。该类有以下几个属性:
Type[] 数组就是该类(我们这里是父类)的泛型,rawType 是原始类型,即 Base 的 Class 类型。而 OwnerType 返回的则是 Base 类型。
然后呢,判断这个 t 是不是 ParameterizedType 接口的实现类。如果是,调用 getActualTypeArguments 方法,返回一个 Type数组,即上图的 actualTypeArguments 属性。
而返回的 Type 数组就是父类的泛型 Class。因为 Class 实现了 Type 接口。为什么是数组呢?因为每个类可以有多个泛型。
通过这样几行代码,我们就得到了泛型。当然,这种用法很少。
现在我们知道了如何得到泛型,那么,就将刚刚的场景中的问题解决。
实现反序列化方法:
1 | private Class<T> deSerializable() { |
获取到当前类(Son)的泛型 Class,获取到泛型数组,返回第一个(因为我们只有一个泛型)泛型类型的 Class
然后,使用 Json 工具传入 Json 字符串和 Class
这样就能够保证编译不会错误,且高度灵活。
这里有一个地方需要注意:Java 的泛型是会在运行期擦除的,但并不总是擦除成 Object ,而是擦除到上限类型。
如果时获取接口的泛型则是调用 Class 的 getGenericInterfaces 方法得到接口集合的泛型。
总结
因为历史原因,Java 的泛型一直是个痛点,但无法避免,所以使用起来确实有点麻烦。但通过 Class 类的众多反射功能,我们还是能够处理泛型的问题。
我们今天使用反射得到了一个类的泛型,并在父类进行处理,成功的将一个字符串反序列化成一个对象。
bye!