泛型的核心作用是避免运行时类型转换异常、减少冗余强转代码、支持同一逻辑适配多种类型;最常用形式如List<String>,解决原始集合取元素需手动强转易抛ClassCastException的问题。

直接从“为什么需要泛型”开始学,比死记语法更高效。Java 1.5 引入泛型,核心就三点:避免运行时类型转换异常、减少冗余强转代码、让同一套逻辑适配多种类型。学的时候别一上来就啃通配符或上下界,先抓住最常用、最实在的用法。
先搞懂泛型类怎么用
泛型类是新手接触最多的形式,比如 List<String>、Box<Integer>。关键不是记住写法,而是理解它解决了什么问题:
- 不加泛型的
List可以存任何对象,但取出来必须手动强转,稍不注意就抛ClassCastException - List<String> 里 add(123) 直接报错,根本进不了运行期
- 定义自己的泛型类时,类型参数(如
<T>)不能是基本类型(int不行,得用Integer) - 创建实例时,JDK 7+ 支持钻石操作符
<>,比如new ArrayList<>(),编译器能自动推断
再掌握泛型方法的独立性
泛型方法和所在类是否泛型无关,它自己就能决定类型。这是很多初学者卡壳的地方——误以为只有泛型类里才能写泛型方法。
- 静态方法想用泛型,必须自己声明类型参数,例如:
public static <T> void print(T item) - 非静态方法可以复用类的泛型参数,也可以另起一套,比如类是
<K, V>,方法可以是<T> T convert(...) - 泛型方法调用时,类型通常由实参推断,不需要显式写
print<String>("hi"),直接print("hi")就行
理解擦除机制,避开常见坑
Java 泛型是“伪泛型”,只在编译期起作用,运行时类型信息被擦除。这个特性直接影响编码方式:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
立即学习“Java免费学习笔记(深入)”;
-
ArrayList<String>.class == ArrayList<Integer>.class返回 true —— 因为运行时都是ArrayList - 不能用
if (t instanceof T)判断泛型类型,也不能new T()创建实例(类型已不存在) - 需要运行时类型信息时,得靠传入
Class<T>对象,比如public <T> T create(Class<T> clazz) - 泛型数组不允许直接创建,
new ArrayList<String>[10]编译失败,要用new ArrayList[10]然后转型(带警告)或改用容器
逐步接触通配符和边界
等上面三块练熟了,再碰 ? extends T 和 ? super T。它们本质是为了解决“子类型集合能否赋值给父类型引用”这类问题:
-
List<? extends Number>表示“某个 Number 子类型的列表”,可读不可写(除了null) -
List<? super Integer>表示“某个 Integer 父类型的列表”,可写 Integer,读出来只能当 Object - 没边界限制的
List<?>几乎只用于工具方法,比如打印任意列表:static void print(List<?> list) - 不要为了用而用,多数业务代码里,明确写
List<String>比List<?>更清晰、安全
不复杂但容易忽略:泛型不是万能的,它解决的是编译期类型安全,不是运行时多态。真正要灵活处理不同行为,还得靠接口抽象和策略模式。把泛型当成“类型检查的保险丝”,而不是“万能胶水”,学起来反而轻松。

















