Java中不能直接用Class作泛型参数,但可通过Type Token模式利用匿名子类保留泛型信息实现类型安全动态实例化,其核心是通过getGenericSuperclass()提取ParameterizedType。

Java 中不能直接用 Class 作为泛型类型参数(比如 Token<T> 的 T 不能是运行时才确定的 Class<?>),但可以通过“类型令牌(Type Token)”模式配合反射,在保留泛型擦除前提下实现类型安全的动态实例化。
理解 Type Token 的核心思想
泛型在编译后被擦除,但 java.lang.reflect.Type(尤其是 ParameterizedType)可在运行时携带泛型信息。Type Token 本质是一个匿名子类的 Class 对象,利用其 getGenericSuperclass() 提取父类的完整泛型签名。
- 它不是把
Class当作泛型参数,而是用子类的继承关系“固化”类型信息 - 典型写法:
new TypeToken<List<String>>() {},此时匿名类的父类型就是带泛型的List<String> -
TypeToken类本身不参与泛型擦除——它的子类类型信息保留在字节码中
手写轻量级 TypeToken 支持动态实例化
不依赖 Guava,可自定义一个支持类型推导与构造器调用的 TypeToken:
public abstract class TypeToken<T> {
private final Type type;
protected TypeToken() {
Type superClass = getClass().getGenericSuperclass();
if (superClass instanceof ParameterizedType) {
this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
} else {
throw new IllegalArgumentException("TypeToken must be created with anonymous subclass");
}
}
public Type getType() { return type; }
@SuppressWarnings("unchecked")
public T newInstance() throws Exception {
if (type instanceof Class) {
return ((Class<T>) type).getDeclaredConstructor().newInstance();
}
throw new UnsupportedOperationException("Cannot instantiate non-raw type: " + type);
}
}
使用示例:
立即学习“Java免费学习笔记(深入)”;
TypeToken<ArrayList<Integer>> token = new TypeToken<ArrayList<Integer>>() {};
ArrayList<Integer> list = token.newInstance(); // 类型安全,编译期已知 T 是 ArrayList<Integer>
结合 Class 参数做运行时泛型推断(有限场景)
若必须传入 Class<?>(如从配置读取类名),无法恢复泛型参数,但可约束原始类型并手动补全:
- 只支持无参构造、非泛型原始类型(如
String.class、MyBean.class) - 泛型类型需显式传入
Type(如new ParameterizedTypeImpl(...))或使用 TypeToken 子类封装 - 推荐组合方式:
TypeToken.of(MyBean.class)返回TypeToken<MyBean>,再调用newInstance()
避免常见陷阱
泛型类型安全不是魔法,关键在编译期绑定与运行时校验协同:
- 不要写
new TypeToken<T>() {}(T 是类型变量)——编译失败,因无法捕获泛型参数 - 不要试图用
Class<T>替代TypeToken<T>做泛型实例化——会丢失List<String>中的String - 反射创建对象前检查
isInterface()和isEnum(),避免InstantiationException - 生产环境建议配合模块化验证:对
Type做白名单校验,防止反序列化攻击


















