当前位置: 首页 > news >正文

泛型类型在编译后会因类型擦除如何找到原始类型

目录
  • 背景和价值
      • 📌 一、通过反射获取父类/接口的泛型类型
      • 🔍 二、使用TypeToken模式(如Gson库)
      • 🧩 三、通过字段或方法签名获取泛型类型
      • ⚠️ 四、通过对象实例推断类型(局限性)
      • ⚙️ 五、类型擦除的原理与限制
      • 💎 总结:如何选择合适方案
  • 参考资料

背景和价值

在Java中,泛型类型在编译后会因类型擦除(Type Erasure)丢失具体类型信息(如List<String>编译后变为List),但仍有多种方法可在运行时获取原始泛型类型。以下是常用的技术方案及原理:


📌 一、通过反射获取父类/接口的泛型类型

适用场景:泛型类被具体子类继承时,子类可获取父类声明的泛型类型。
原理:子类的Class对象会保留父类泛型参数的实际类型,通过getGenericSuperclass()可获取ParameterizedType,进而提取类型参数。
代码示例

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;public abstract class GenericType<T> {private final Class<T> type;public GenericType() {Type superClass = getClass().getGenericSuperclass();ParameterizedType pt = (ParameterizedType) superClass;this.type = (Class<T>) pt.getActualTypeArguments()[0]; // 获取第一个泛型参数}public Class<T> getType() { return type; }
}// 使用子类指定具体类型
public class StringType extends GenericType<String> {}// 测试
StringType obj = new StringType();
System.out.println(obj.getType()); // 输出: class java.lang.String

注意:此方法要求子类必须显式继承并指定泛型类型(如StringType extends GenericType<String>)。


🔍 二、使用TypeToken模式(如Gson库)

适用场景:需动态获取复杂泛型类型(如List<Map<String, Integer>>)。
原理:通过匿名子类捕获泛型信息,利用TypeToken保存ParameterizedType
代码示例(使用Gson的TypeToken):

import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;public class Main {public static void main(String[] args) {Type type = new TypeToken<List<String>>() {}.getType(); // 匿名子类保留泛型System.out.println(type); // 输出: java.util.List<java.lang.String>}
}

优势:支持嵌套泛型,广泛用于JSON序列化(如Gson)、依赖注入框架。


🧩 三、通过字段或方法签名获取泛型类型

适用场景:类中定义了泛型字段或方法,需在运行时解析其类型。
原理:反射API可获取字段/方法的泛型签名(getGenericType()getGenericReturnType())。
代码示例

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.List;public class DataHolder {private List<Integer> numbers;public static void main(String[] args) throws Exception {Field field = DataHolder.class.getDeclaredField("numbers");ParameterizedType pt = (ParameterizedType) field.getGenericType();Class<?> type = (Class<?>) pt.getActualTypeArguments()[0];System.out.println(type); // 输出: class java.lang.Integer}
}

适用场景:解析类成员或方法的泛型声明,如ORM框架处理泛型集合字段。


⚠️ 四、通过对象实例推断类型(局限性)

适用场景:泛型对象已存储数据,需通过数据反推类型。
方法:调用obj.getClass()或元素对象的getClass(),但仅能获取擦除后的原始类型(如ArrayList而非ArrayList<String>)。
代码示例

List<String> list = new ArrayList<>();
list.add("test");
Class<?> elementType = list.get(0).getClass(); // 获取String类型
System.out.println(elementType); // 输出: class java.lang.String

局限

  1. 集合为空时无法获取类型;
  2. 只能获取元素运行时类型(可能是子类,不一定是声明类型)。

⚙️ 五、类型擦除的原理与限制

根本原因:Java泛型为兼容旧版本,采用编译时检查 + 运行时擦除(替换为Object或类型上界)。例如:

// 编译前
List<String> list = new ArrayList<>();
// 编译后(字节码)
List list = new ArrayList(); // 类型参数被擦除

后果:运行时无法直接通过List.class获取String类型。


💎 总结:如何选择合适方案

方法 适用场景 关键限制
反射获取父类泛型 子类明确继承泛型父类 需设计继承关系
TypeToken 动态解析嵌套泛型(如JSON、RPC) 依赖第三方库(如Gson)
反射解析字段/方法签名 类成员或方法包含泛型声明 需存在泛型字段或方法
对象实例推断 集合非空且需元素实际类型 无法获取声明类型,空集合失效

实际应用

  • 序列化框架(如Gson)用TypeToken解析List<T>
  • Spring依赖注入通过泛型父类自动匹配Repository<User>
  • ORM框架解析实体类的泛型字段类型。

建议优先使用TypeToken父类反射,二者直接关联泛型声明,避免运行时类型不安全问题。

参考资料

http://www.njgz.com.cn/news/135.html

相关文章:

  • 《大道至简》
  • 入参有泛型,返回值为什么必须有T
  • MySQL--索引
  • day3
  • Pipal密码分析工具的模块化检查器与分割器系统详解
  • 练习224A. Parallelepiped
  • 动态规划从精通到入门
  • 树形DP-Part 1
  • TRVCOST - Travelling cost 题解
  • 第一天
  • 111
  • 10
  • 7.26 4
  • DAY22
  • 30天总结-第二十六天
  • 周末
  • foobar2000 v2.24.6 汉化版
  • 今天做什么
  • 20天
  • OI集训 Day10
  • 【leetcode刷题】动态规划 Part4 经典线性DP
  • linux快照工具 timeshift
  • 关于LCD屏幕硬件参数
  • 今日总结
  • 莫队二次离线 学习笔记
  • 运算符
  • 进度
  • 软工7.26
  • DMY集训记录