java泛型核心是类型擦除:编译期检查并擦除泛型信息为object或上界类型,插入强制转换,运行时仅剩原始类型,导致无法instanceof判断、不能new t()等限制。

一、先看它解决了什么问题
没有泛型时,集合就是“筐”,什么都能装,取出来全靠人猜和强转:
-
类型不安全:往
List里加了String和Integer,取String时却拿到Integer,运行时报ClassCastException -
代码冗余:每次
get()都要手动写(String) list.get(0) -
语义模糊:看到
List不知道它该存什么,靠注释或上下文推测
泛型把“类型”变成参数,让编译器替你盯住这件事——编译期检查 + 自动转型 + 明确语义,三件事一次到位。
二、重点拆透类型擦除机制
泛型不是 JVM 原生支持的特性,而是编译器做的“翻译工作”。所谓“擦除”,是指:所有泛型信息在字节码里都被抹掉,只留下原始类型(Raw Type)。
-
无界泛型(如
<t></t>)→ 擦成Object:public class Box<t> { T value; }</t>编译后等价于public class Box { Object value; } -
有上界泛型(如
<t extends number></t>)→ 擦成上界类型:T get()擦成Number get(),不是Object -
编译器悄悄补活:自动在返回值位置插入
checkcast指令(相当于隐式强转),保证你用起来像真的一样安全
验证方式很简单:用反射打印 new ArrayList<string>().getClass() == new ArrayList<integer>().getClass()</integer></string>,结果是 true——说明运行时根本没“String”“Integer”之分。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
三、看清限制,避开典型坑
正因为擦除,以下操作在运行时都不可行:
-
不能用
instanceof判断泛型类型:if (list instanceof ArrayList<string>)</string>编译不通过 -
不能直接
new T():因为运行时不知道T是什么类,连构造函数都找不到 -
静态方法/字段不能引用类型参数
T:静态属于类级别,而泛型类型是实例级别的,二者生命周期不匹配 -
不能创建泛型数组:
new ArrayList<string>[10]</string>编译报错;必须写成(ArrayList<string>[]) new ArrayList[10]</string>(带警告)
这些不是 bug,是擦除机制下的必然结果。接受它,才能写出健壮代码。
四、实用技巧:绕过擦除做真正类型感知
当确实需要运行时类型信息(比如 JSON 反序列化、通用 DAO),就得主动保留:
-
传入
Class<t></t>对象:public <t> T fromJson(String json, Class<t> clazz)</t></t>
这样就能用clazz.getDeclaredConstructor().newInstance()创建实例 -
用
TypeReference(Jackson)或ParameterizedType(反射)捕获泛型签名:
例如new TypeReference<list>>() {}</list>,利用匿名子类保留类型信息 -
泛型方法配合通配符灵活协变/逆变:
void copy(List extends Number> src, List super Integer> dst)—— 理解? extends和? super的边界逻辑,比死记规则更重要
本质上,这些技巧都是在编译期“多记一笔”,把被擦掉的信息,以其他形式重新带进运行时。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










