java泛型擦除是编译期行为,运行时退化为原始类型,但可通过class显式传参或typereference/typetoken捕获泛型信息,在擦除前提下安全还原包装类语义,并需警惕拆箱空指针、精度丢失及整数缓存陷阱。

明确泛型擦除后的真实类型局限
擦除后 JVM 看不到 `
- 元素实际运行时的 实例类型(如 `jsonStr` 解析后某元素是 `Integer` 实例,不是 `String`)
- 你 已知的业务契约(例如:这个字段本应是 `List
`,不是 `List - 泛型声明在 字节码元数据中部分残留(仅限于类/方法签名、字段定义处,可通过反射获取;但集合内容本身无此信息)
用 Class 显式传入单层包装类类型
适用于已知具体包装类、且不嵌套泛型的场景(如 `UserDTO.id` 是 `Long`,或 `List
- 方法签名接收 `Class
` 参数,用于运行时类型校验与转换 - 对每个原始值(如 `Object raw = list.get(i)`)调用 `clazz.cast(raw)` 或手动判断后转型
- 对基本包装类,可结合 `Number` 抽象统一处理数字类型
public static <t> T safeCast(Object value, Class<t> targetType) {<br> if (value == null) return null;<br> if (targetType == Integer.class && value instanceof Number) return targetType.cast(((Number)value).intValue());<br> if (targetType == Boolean.class && value instanceof Boolean) return targetType.cast(value);<br> // ... 其他包装类分支<br> throw new ClassCastException("Cannot cast " + value + " to " + targetType);<br>}</t></t>
用 TypeReference / TypeToken 捕获嵌套泛型中的包装类
当目标是 `List` 完全无法区分内部类型。此时必须借助匿名内部类保留泛型签名:
- Jackson 使用
new TypeReference<list>>() {}</list>—— 编译后该匿名类的父类签名含完整 `- >`,反射可提取 `Integer.class`
- Gson 使用
new TypeToken<map double>>() {}.getType()</map>—— 同理获取 `Double.class` - 拿到 `Type` 后,JSON 库内部会递归解析每个元素,并对数值型节点按 `Double`/`Integer` 等包装类规则反序列化,而非默认 `Double` 或 `BigDecimal`
拆箱与装箱要警惕空指针和精度丢失
还原出包装类对象后,若需转为基本类型(如 `int`、`double`),必须注意:
- 自动拆箱遇 null 会抛 NPE:`Integer i = null; int x = i;` → 直接崩溃。应先判空或用 `i != null ? i : 0`
- 数值包装类间转换可能丢失精度:`Long l = 1234567890123456789L; double d = l.doubleValue();` 可能因 double 尾数位不足而失真
- 整数缓存陷阱:`Integer.valueOf(100)` 返回缓存对象,`==` 比较可能为 true;但 `valueOf(200)` 就是新对象,`==` 为 false —— 始终用 `.equals()` 比较值











