1. 反射定义

2. 反射方法

2.1. 获取集合对象中泛型的类型[^获取集合对象中泛型的类型]

  1. 泛型的概念

    泛型概念

    泛型:参数化类型,也就是说要操作的数据一开始不确定是什么类型,在使用的时候,才能确定是什么类型,把要操作的数据类型指定为一个参数,在使用时传入具体的类型,实现代码的模板化。

  2. 获取集合泛型时遇到的问题

    1. 在学习JDBC设计BaseDao<T>时类遇到了一个需要在创建子类对象时给父类BaseDao<T>赋上泛性类型的案例,具体代码实现如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      public class BaseDao<T> {
      private QueryRunner queryRunner = new QueryRunner();
      // 定义一个变量来接收泛型的类型
      private Class<T> type;

      // 获取T的Class对象,获取泛型的类型,泛型是在被子类继承时才确定(难点!!!)
      public BaseDao() {
      // 获取子类的类型
      Class clazz = this.getClass();//这里的this表示子类,因为子类在创建对象时,必须先加载父类构造器
      // 获取父类的类型
      // getGenericSuperclass()用来获取当前类的父类(泛型)的类型
      // ParameterizedType表示的是带泛型的类型
      ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericSuperclass();
      // 获取具体的泛型类型 getActualTypeArguments获取具体的泛型的类型
      // 这个方法会返回一个Type的数组
      Type[] types = parameterizedType.getActualTypeArguments();
      // 获取具体的泛型的类型·
      this.type = (Class<T>) types[0];
      }
    2. 此时,我们会很自然的联想到集合是否也可以 通过new子类对象确定该集合父类对象的泛型,然后在通过getGenericSuperclass()方法来得到泛型类型呢?(以ArrayList集合为例)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      public class GetGenericType {
      private static List<String> arr = new ArrayList<>();

      public static void main(String[] args) throws Exception {

      arr.add("zhangsan");
      Type genericSuperclass = arr.getClass().getGenericSuperclass();
      ParameterizedType pty= (ParameterizedType) genericSuperclass;
      System.out.println(pty.getActualTypeArguments()[0]);//E

      }

      然而这结果却是泛型的定义格式E,然后我去看ArrayList<E>的直接父类AbstractList<E>源码中并没有类似自定义的BaseDao<T>类中的泛型属性type
      arr.getClass().getGenericSuperclass()获取泛型时输出的结果是原始类型,而不是你真实new ArrayList对象时传进去的泛型类型,这是因为java编译器是通过先检查代码中泛型的类型,然后再进行类型擦除,再进行编译的。

    3. Java的泛型是如何工作的 ? 什么是类型擦除 ?
      泛型的正常工作是依赖编译器在编译源码的时候,先进行类型检查,然后进行类型擦除并且在类型参数出现的地方插入强制转换的相关指令实现的。

    编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如List<String>在运行时仅用一个List类型来表示。为什么要进行擦除呢?这是为了避免类型膨胀。

  3. 解决方案
    此时就需要利用反射的动态性来解决,解决代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class GetGenericType {
    private static List<String> arr = new ArrayList<>();

    public static void main(String[] args) throws Exception {

    Class<GetGenericType> aClass = GetGenericType.class;
    //通过反射获取arr属性
    Field arr1 = aClass.getDeclaredField("arr");
    //通过属性获取泛型
    Type genericType = arr1.getGenericType();
    // ParameterizedType表示的是带泛型的类型
    ParameterizedType pt = (ParameterizedType) genericType;
    // 获取具体的泛型类型 getActualTypeArguments获取具体的泛型的类型
    // 这个方法会返回一个Type的数组
    Type[] actualTypeArguments = pt.getActualTypeArguments();
    System.out.println(genericType);//java.util.List<java.lang.String>
    System.out.println(actualTypeArguments[0]);//class java.lang.String

    }
    }

[^获取集合对象中泛型的类型]: 原文链接: https://blog.csdn.net/weixin_53618874/article/details/118863578