泛型是 Java 5 引入 的语法,核心作用:编译期类型检查、避免强制类型转换、代码复用、类型安全。
一、为什么需要泛型?(痛点)
没有泛型时,集合默认存 Object,取出必须强转,极易报 ClassCastException:
java
// 无泛型:不安全、需强转
ArrayList list = new ArrayList();
list.add("Java");
String str = (String) list.get(0); // 强制转换
泛型限定只能存指定类型,编译就报错,运行更安全。
二、基本语法
1. 泛型集合(最常用)
格式:容器<类型>
java
// 只能存放 String,无需强转
ArrayList<String> list = new ArrayList<>(); // 菱形语法 <> Java7+
list.add("泛型");
String s = list.get(0); // 直接取值,不用强转
2. 泛型标记约定(行业习惯)
<T>:Type 任意类型(最常用)<E>:Element 集合元素<K>:Key 键<V>:Value 值<N>:Number 数字类型
三、1. 泛型类
在类名后定义泛型,整个类可用该类型。
java
// 定义泛型类
public class Box<T> {
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
// 使用
Box<String> box1 = new Box<>();
box1.setData("字符串");
Box<Integer> box2 = new Box<>();
box2.setData(100);
四、2. 泛型方法
方法独立定义泛型 ,优先级高于类泛型,格式:<T> 返回值 方法名()
java
public class GenericDemo {
// 泛型方法
public static <T> void print(T t) {
System.out.println(t);
}
public static void main(String[] args) {
print("Java");
print(666);
print(true);
}
}
五、3. 泛型接口
接口上定义泛型,实现类必须指定类型或继续保留泛型。
java
// 泛型接口
public interface IGeneric<T> {
T getInfo();
}
// 方式1:实现类明确指定类型
class Impl1 implements IGeneric<String> {
@Override
public String getInfo() {
return "指定字符串类型";
}
}
// 方式2:实现类继续保留泛型
class Impl2<T> implements IGeneric<T> {
@Override
public T getInfo() {
return null;
}
}
六、通配符 ?(重点)
? 代表任意未知类型,多用于方法参数。
1. 无界通配符 <?>
匹配任意类型 ,只能读、不能写(除了 null)。
java
public static void show(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
2. 上界通配符 <? extends 父类>
限定:只能是该类 或 它的子类
规则:可读,不可写
java
// 只接收 Number 及子类(Integer、Double、Long)
public static void test(List<? extends Number> list) {}
3. 下界通配符 <? super 子类>
限定:只能是该类 或 它的父类
规则:可写,可读只能用 Object
java
// 只接收 Integer 及父类(Number、Object)
public static void test(List<? super Integer> list) {}
七、泛型边界 & 擦除(面试高频)
1. 泛型上限(限定类型范围)
java
// T 只能是 Animal 或其子类
public class AnimalBox<T extends Animal> {}
2. 类型擦除(Java 泛型本质)
Java 泛型是伪泛型:
- 编译阶段生效,运行时泛型信息会被擦除
List<String>和List<Integer>运行时都是List<Object>- 因此不能用泛型做重载判断、不能创建泛型数组、基本类型不能作泛型
java
// 错误:编译认为两个方法签名一样,泛型被擦除
public void fun(List<String> list){}
public void fun(List<Integer> list){}
3. 泛型不支持基本类型
必须用包装类 :
✅ List<Integer>
❌ List<int>
八、泛型继承规则
List<String> 不是 List<Object> 的子类!
java
List<String> strList = new ArrayList<>();
List<Object> objList = strList; // 编译报错
原因:防止类型污染。
九、总结速记
- 作用:类型安全、免强转、代码复用
- 三类用法:泛型类、泛型方法、泛型接口
- 通配符:
<?>任意类型<? extends T>上界(子类)<? super T>下界(父类)
- 底层:类型擦除,运行无泛型信息
- 禁忌:不能用基本类型、不能基于泛型重载、慎用泛型数组