美文网首页
Java 如何获取泛型类型

Java 如何获取泛型类型

作者: 莫那一鲁道 | 来源:发表于2018-05-04 21:51 被阅读124次

前言

在 Java 开发中,获取泛型这种操作虽不是很常用,但有时确实必须的,比如 将Json 字符串反序列化成对象的时候。今天就来介绍这个操作。

场景

假设我们定义了一个类,内部有一个数据结构,泛型为 T,当我们输入一个 Json 字符串,想把这个 Json 反序列化成对象,那么此时,我们就需要知道这个泛型的类型。

具体代码场景如下:

抽象父类(包含泛型):

abstract class Base<T extends Comparable<T>> {

  T data;

  public Base(String json) {
    this.data = JsonUtil.toObject(json, deSerializable());
  }
}

我们想在该类中输入 Json,并将字符串反序列化成对象。比如下面这样:

/**
 * 子类定义了父类
 */
class Son extends Base<DataClass> {

  public Son(String json) {
    super(json);
  }
}

/**
 * 数据类型继承Comparable
 */
class DataClass implements Comparable<DataClass> {

  @Override
  public int compareTo(DataClass o) {
    return 0;
  }
}

上面的例子中,子类定义了泛型,但获取泛型类型是在父类。

所以,重点在 deSerializable() 方法的实现,我们需要一个 Class<T> 让 Json 工具能够正常序列化。

如何实现?

先说结论:通过 Java 反射包的 ParameterizedType 工具获得泛型具体类型。

例如:下面的代码:

  public static void main(String[] args) {
    String json = JsonUtil.toJson(new DataClass());
    Son s = new Son(json);
    Type t = s.getClass().getGenericSuperclass();
    if (t instanceof ParameterizedType) {
      System.out.println(t);
      // output: cn.think.in.java.clazz.loader.generics.Base<cn.think.in.java.clazz.loader.generics.DataClass>
      for (Type type : ((ParameterizedType) t).getActualTypeArguments()) {
        System.out.println(type);
        //output: class cn.think.in.java.clazz.loader.generics.DataClass
      }
    }
  }

首先我们将一个对象序列化成 Json 字符串,模拟外部输入。然后呢?创建一个子类对象,得到这个 Son 的 Class 。
关键地方来了,调用 getGenericSuperclass 方法,这个方法的作用是:返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type。

所以这里会得到一个 ParameterizedTypeImpl 类型的对象。注意:这个类是 Sun 包下的,不是开源的。该类有以下几个属性:

Type[] 数组就是该类(我们这里是父类)的泛型,rawType 是原始类型,即 Base 的 Class 类型。而 OwnerType 返回的则是 Base 类型。

然后呢,判断这个 t 是不是 ParameterizedType 接口的实现类。如果是,调用 getActualTypeArguments 方法,返回一个 Type数组,即上图的 actualTypeArguments 属性。

而返回的 Type 数组就是父类的泛型 Class。因为 Class 实现了 Type 接口。为什么是数组呢?因为每个类可以有多个泛型。

image.png

通过这样几行代码,我们就得到了泛型。当然,这种用法很少。

现在我们知道了如何得到泛型,那么,就将刚刚的场景中的问题解决。

实现反序列化方法:

  private Class<T> deSerializable() {
    Type type = getClass().getGenericSuperclass();
    if (type instanceof ParameterizedType) {
      ParameterizedType parameterizedType = (ParameterizedType) type;
      System.out.println(parameterizedType.getActualTypeArguments()[0]);
      return (Class<T>) parameterizedType.getActualTypeArguments()[0];
    }
    throw new RuntimeException();
  }

获取到当前类(Son)的泛型 Class,获取到泛型数组,返回第一个(因为我们只有一个泛型)泛型类型的 Class<T>。
然后,使用 Json 工具传入 Json 字符串和 Class<T> 类型并返回实体对象。

这样就能够保证编译不会错误,且高度灵活。

这里有一个地方需要注意:Java 的泛型是会在运行期擦除的,但并不总是擦除成 Object ,而是擦除到上限类型。
如果时获取接口的泛型则是调用 Class 的 getGenericInterfaces 方法得到接口集合的泛型。

总结

因为历史原因,Java 的泛型一直是个痛点,但无法避免,所以使用起来确实有点麻烦。但通过 Class 类的众多反射功能,我们还是能够处理泛型的问题。

我们今天使用反射得到了一个类的泛型,并在父类进行处理,成功的将一个字符串反序列化成一个对象。

bye!

相关文章

  • java 泛型

    1,如何实例化泛型 2,如何获取java中的泛型类型 调用(通常在构造方法中调用):

  • Java如何在运行时获取泛型的类型

    Java泛型是伪泛型,会在编译完成时进行类型的擦除,我们无法在运行时获取泛型参数的具体类型(类型擦除会被替换成泛型...

  • Java泛型的原始类型

    众所周知,Java中的泛型在编译期被擦除,那有没有办法在运行时获取到泛型的原始类型呢?有的。 获取泛型类型 如果定...

  • Java 如何获取泛型类型

    前言 在 Java 开发中,获取泛型这种操作虽不是很常用,但有时确实必须的,比如 将Json 字符串反序列化成对象...

  • Java泛型教程

    Java泛型教程导航 Java 泛型概述 Java泛型环境设置 Java泛型通用类 Java泛型类型参数命名约定 ...

  • 【进阶之路】Java的类型擦除式泛型

    【进阶之路】Java的类型擦除式泛型 Java选择的泛型类型叫做类型擦除式泛型。什么是类型擦除式泛型呢?就是Jav...

  • Java 获取泛型类型

    不太完美的泛型 大家在使用泛型开发的时候,有很多时候会遇到这样的需求—— 获取泛型具体的类。比如现在有Servic...

  • 泛型

    JAVA 如何实现泛型(参数化的类型) 为什么需要泛型 1、多种数据类型执行相同的代码image.png 2、泛型...

  • Java如何优雅获取泛型类型

    在 Java 开发中,获取泛型这种操作虽不是很常用,但有时确实必须的,比如 将Json 字符串反序列化成对象的时候...

  • java泛型

    java泛型 Java 泛型的参数只可以代表类,不能代表个别对象。 由于 Java 泛型的类型参数之实际类型在编译...

网友评论

      本文标题:Java 如何获取泛型类型

      本文链接:https://www.haomeiwen.com/subject/egfjrftx.html