天天看点

Java:如何生成类的泛型参数的实例?

要生成类的泛型类型参数的实例,先要生成类的泛型类型参数的Class对象。比如,对于泛型类Testable<T, S>,它的T、S这两个类型参数的Class对象:Class<T>, Class<S>。

public class Testable<T, S> {

  private Class<T> tClazz;
  private Class<S> sClazz;

  public Testable() {
  }

  public Class<T> getTClazz() {
    return tClazz;
  }

  public Class<S> getSClazz() {
    return sClazz;
  }
}
           

网上可以找到的一般性的处理方法如下:

public static List<Class<?>> getTypeArgumentClassList(Class<?> genericClass) {
  Type rawType = genericClass.getGenericSuperclass();
  ParameterizedType o = (ParameterizedType) rawType;

  Type[] typeArguments = o.getActualTypeArguments();
  List<Class<?>> typeArgumentClassList = new ArrayList<Class<?>>();

  for (Type t : typeArguments) {
    Class<?> clazz = (Class<?>) t;
    typeArgumentClassList.add(clazz);
  }

  return typeArgumentClassList;
}
           

测试代码:

public static void doTest() {
    Testable<String, Integer> g = new Testable<String, Integer>();

    for (Class<?> typeArgumentClazz : getTypeArgumentClassList(g.getClass())) {
      System.out.println(typeArgumentClazz + " : \t" + typeArgumentClazz.getCanonicalName());
    }
  }
           

运行后报错:

Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType

原因是Class.getGenericSuperclass()是去获取泛型父类的Type对象,而Testable<T, S>没有泛型父类,所以报错了。由于JDK中没有类似于Class.getGenericClass()这样的方法,所以只能想别的办法解决这个问题:

Testable<String, Integer> g = new Testable<String, Integer>() {};

加上{}后,这行代码的意思是创建Testable<String, Integer>()匿名子类,然后创建这个匿名子类的对象,这样对这个匿名子类调用Class.getGenericSuperclass()就不会有问题,因为它的泛型父类就是Testable<T, S>。

public static void doTest() {
  Testable<String, Integer> g = new Testable<String, Integer>(){};

  for (Class<?> typeArgumentClazz : getTypeArgumentClassList(g.getClass())) {
    System.out.println(typeArgumentClazz + " : \t" + typeArgumentClazz.getCanonicalName());
  }
}
           

代码运行结果:

class java.lang.String :   java.lang.String

class java.lang.Integer :   java.lang.Integer

如果测试代码换成:

public static void doTest() {
  Testable<String, List<?>> g = new Testable<String, List<?>>() {
  };

  for (Class<?> typeArgumentClazz : getTypeArgumentClassList(g.getClass())) {
    System.out.println(typeArgumentClazz + " : \t" + typeArgumentClazz.getCanonicalName());
  }
}
           

代码报错:

Exception in thread "main" java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl cannot be cast to java.lang.Class

此报错说明,List<?>对应的Type对象实际上是java.lang.reflect.ParameterizedType,不能直接转化为Class<?>,因此,修改代码如下:

public static List<Class<?>> getTypeArgumentClassList(Class<?> genericClass) {
  Type rawType = genericClass.getGenericSuperclass();
  ParameterizedType o = (ParameterizedType) rawType;

  Type[] typeArguments = o.getActualTypeArguments();
  List<Class<?>> typeArgumentClassList = new ArrayList<Class<?>>();

  for (Type t : typeArguments) {
    Class<?> clazz = null;

    if (t instanceof Class<?>) {
      clazz = (Class<?>) t;
    } else {
      Type innerType = ((ParameterizedType) t).getRawType();
      clazz = (Class<?>) innerType;
    }
    typeArgumentClassList.add(clazz);
  }
  return typeArgumentClassList;
}
           

运行结果:

class java.lang.String :   java.lang.String

interface java.util.List :   java.util.List

如果测试代码换成:

public static void doTest() {
  Testable<String, List<?>[]> g = new Testable<String, List<?>[]>() {
  };

  for (Class<?> typeArgumentClazz : getTypeArgumentClassList(g.getClass())) {
    System.out.println(typeArgumentClazz + " : \t" + typeArgumentClazz.getCanonicalName());
  }
}
           

代码报错:

Exception in thread "main" java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl cannot be cast to java.lang.reflect.ParameterizedType

此报错说明,List<?>[]对应的Type对象实际上是java.lang.reflect.GenericArrayType不是java.lang.reflect.ParameterizedType,因此,修改代码如下:

public static List<Class<?>> getTypeArgumentClassList(Class<?> genericClass) {
  Type rawType = genericClass.getGenericSuperclass();
  ParameterizedType o = (ParameterizedType) rawType;

  Type[] typeArguments = o.getActualTypeArguments();
  List<Class<?>> typeArgumentClassList = new ArrayList<Class<?>>();

  for (Type t : typeArguments) {
    Class<?> clazz = null;

    if (t instanceof Class<?>) {
      clazz = (Class<?>) t;
    } else if (t instanceof ParameterizedType) {
      Type innerType = ((ParameterizedType) t).getRawType();
      clazz = (Class<?>) innerType;
    } else if (t instanceof GenericArrayType) {
      Type compType = ((GenericArrayType) t).getGenericComponentType();
      Type innerType = ((ParameterizedType) compType).getRawType();
      Class<?> compClazz = (Class<?>) innerType;

      clazz = Array.newInstance(compClazz, 0).getClass();
    }
    typeArgumentClassList.add(clazz);
  }
  return typeArgumentClassList;
}
           

运行结果:

class java.lang.String :   java.lang.String

class [Ljava.util.List; :   java.util.List[]

由于List<?>[]是一个数组,它的Class对象的获取可能通过:

Array.newInstance(compClazz, 0).getClass();

如果测试代码换成:

public static void doTest() {
  Testable<String, List<String>[][]> g = new Testable<String, List<String>[][]>() {
  };

  for (Class<?> typeArgumentClazz : getTypeArgumentClassList(g.getClass())) {
    System.out.println(typeArgumentClazz + " : \t" + typeArgumentClazz.getCanonicalName());
  }
}
           

代码报错:

Exception in thread "main" java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl cannot be cast to java.lang.reflect.ParameterizedType

此报错的原因是,compType实现上是List<String>[]对应的Type对象,它还是java.lang.reflect.GenericArrayType不是java.lang.reflect.ParameterizedType,因而还需要再做一次((GenericArrayType) t).getGenericComponentType();

但如果是Testable<String, List<String>[][][]>、Testable<String, List<String>[][][][]>、.......,那就不能解决了。对以上代码重构后,提供完整实现如下:

import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public final class GenericTools {

  public static List<Class<?>> getTypeArgumentClassList(Class<?> genericClass) {
    Type rawType = genericClass.getGenericSuperclass();
    if (rawType instanceof Class<?>) {
      return Collections.emptyList();
    }

    ParameterizedType o = (ParameterizedType) rawType;

    Type[] typeArguments = o.getActualTypeArguments();
    List<Class<?>> typeArgumentClassList = new ArrayList<Class<?>>();

    for (Type typeArgument : typeArguments) {
      typeArgumentClassList.add(getTypeArgumentClass(typeArgument));
    }

    return typeArgumentClassList;
  }

  public static Class<?> getTypeArgumentClass(Type typeArgument) {
    if (typeArgument instanceof Class<?>) {
      return (Class<?>) typeArgument;
    } else if (typeArgument instanceof ParameterizedType) {
      Type innerType = ((ParameterizedType) typeArgument).getRawType();
      return getTypeArgumentClass(innerType);
    } else if (typeArgument instanceof GenericArrayType) {
      Type compType = ((GenericArrayType) typeArgument).getGenericComponentType();
      Class<?> compClazz = getTypeArgumentClass(compType);

      if (compClazz != null) {
        return Array.newInstance(compClazz, 0).getClass();
      }
    }

    return null;
  }
}
           

运行结果:

class java.lang.String :   java.lang.String

class [[Ljava.util.List; :   java.util.List[][]

最终,对Testable<T, S>修改如下:

import java.util.List;

public class Testable<T, S> {

  private Class<T> tClazz;
  private Class<S> sClazz;

  @SuppressWarnings("unchecked")
  public Testable() {
    List<Class<?>> clazzList = GenericTools.getTypeArgumentClassList(this.getClass());

    tClazz = (Class<T>) clazzList.get(0);
    sClazz = (Class<S>) clazzList.get(1);
  }

  public Class<T> getTClazz() {
    return tClazz;
  }

  public Class<S> getSClazz() {
    return sClazz;
  }
}
           

这样就可以获取到T, S两个泛型类型的Class对象,为后续对这些泛型参数实例化提供了条件。

参考文档

Java Reflection GenericArrayType Example

https://www.javarticles.com/2016/02/java-reflection-genericarraytype-example.html

How to create a generic array?

https://stackoverflow.com/questions/18581002/how-to-create-a-generic-array

How to create a generic array in Java?

https://stackoverflow.com/questions/529085/how-to-create-a-generic-array-in-java