711 字
4 分钟
Java泛型擦除的问题与解决方法
Java 的泛型是一种“语法糖”,它的设计理念是:编译时类型检查,运行时类型擦除。这带来了类型安全,但也引发了一些实际问题。下面详细讲解泛型擦除的原理、常见问题及解决办法。
- 编译时类型检查
在编译阶段,Java 编译器会根据泛型参数帮你做类型检查。例如:
List<String> list = new ArrayList<>();list.add("hello"); // 编译通过list.add(123); // 编译报错,类型不匹配
此时,
- 运行时类型擦除
Java 编译后,泛型类型信息会被“擦除”:
- List
变成 List - Map<Integer, String> 变成 Map
运行时,JVM 并不知道泛型的具体类型,只知道原始类型。例如:
List<String> list = new ArrayList<>();list.add("hello");String s = list.get(0);
JVM 只知道 list 是个 List,get(0) 返回的是 Object,编译器会自动插入类型转换代码。
-
普通代码中,泛型擦除通常无影响
大多数情况下,泛型擦除不会影响代码运行,因为类型检查和转换都在编译阶段完成了。 -
反射和序列化时,泛型擦除导致问题
但在反射和序列化框架(如 Jackson)中,运行时需要确切的类型信息。如果泛型被擦除,框架无法推断出泛型的真实类型,导致反序列化失败。
典型问题举例
假设有如下泛型类:
@Datapublic class ApiResponse<T> { private int code; private String message; private T data;}
你这样调用:
ApiResponse<List<String>> response = restTemplate.exchange( url, // 请求的 URL 地址 HttpMethod.GET, // 请求方法是 GET new HttpEntity<>(headers), // 请求头(HttpEntity 封装了 headers) ApiResponse.class // 指定响应体类型(⚠️ 这里是关键)).getBody(); // 获取响应体内容
此时,Jackson 只知道你要反序列化成 ApiResponse,但不知道 T 是什么类型。于是 data 字段会被反序列化成 LinkedHashMap 或 ArrayList
Java泛型擦除的问题与解决方法
https://htglgithub.github.io/AstroBlog/posts/20250520/