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
Java Reflection GenericArrayType Example - Java Articles

How to create a generic array?
java - How to create a generic array? - Stack Overflow

How to create a generic array in Java?
How to create a generic array in Java? - Stack Overflow

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值