一、泛型
1.1 什么是泛型?
泛型(Generics)是 Java 5 引入的一种机制,允许在定义类、接口或方法时使用"类型参数"(Type Parameter),从而在编译期提供类型安全检查,并避免强制类型转换。
💡 简单理解:
- 泛型 = "参数化类型"
- 就像方法可以接收参数一样,类/方法也可以接收"类型"作为参数


1.2 为什么需要泛型?------ 解决三大痛点
| 问题 | 没有泛型 | 有泛型 |
|---|---|---|
| 1. 类型安全 | 运行时才报错(如 ClassCastException) |
编译期检查,提前发现问题 |
| 2. 强制类型转换 | 需要手动 (String) obj 转换 |
自动类型推断,无需强转 |
| 3. 代码复用性差 | 为不同类型写重复代码 | 一套代码适配多种类型 |
✅ 泛型 = 编译期的类型安全 + 更简洁的代码

Java中的泛型是伪泛型,集合的限定是字符串内,仅仅只是在编译的时候检查是否是字符串,当数据真正添加到集合中的时候,集合还是会把这些数据当成Object类型来处理。拿出来用的时候,底层会做一个强转成字符串类型。
Java 的泛型是"伪泛型"------只存在于编译期,运行时会被"擦除"为原始类型(Raw Type)。
⚠️ 为什么设计成类型擦除?
- 兼容 Java 5 之前的代码(向后兼容)
- 避免 JVM 大改(不需要为每种泛型生成新类)
💡 正因如此,泛型不能用于基本类型 (如
List<int>❌),必须用包装类(List<Integer>✅)


1.3 泛型的核心语法

泛型类

自己编写的时候,只能用<E>代表不确定是什么泛型,只用到别人调用了你写的泛型类才能确定。

java
package com.lkbhua.Test;
import java.util.Arrays;
// 带有泛型的类
public class MyArraysList<E> {
Object[] obj = new Object[10];
int size = 0;
// E:不确定的类型,该类型在类名后面已经定义过了
// e:形参的名字,变量名
public boolean add (E e){
obj[size] = e;
size++;
return true;
}
public E get(int index){
// 强转
return (E) obj[index];
}
// 打印对象的时候,出现的不是地址值,而是属性值
@Override
public String toString() {
// 把数组里面所有的元素都转换成字符串拼接进行返回
return Arrays.toString(obj);
}
}
java
package com.lkbhua.Test;
public class MyArraysTest1 {
public static void main(String[] args) {
MyArraysList<String> list = new MyArraysList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
System.out.println(list);
}
}
泛型方法




java
package com.lkbhua.Test;
import java.util.ArrayList;
public class ListUtil {
private ListUtil(){}
// 类中定义一个静态方法addAll,用来添加多个集合的元素
// 参数一:集合
// 参数二:要添加的元素
//
public static <E>void addAll(ArrayList<E> list ,E e1,E e2,E e3,E e4){
list.add(e1);
list.add(e2);
list.add(e3);
list.add(e4);
}
}
java
package com.lkbhua.Test;
import java.util.ArrayList;
public class GenericsDemo {
public static void main(String[] args) {
/*泛型方法的练习:
定义一个工具类:ListUtil
类中定义一个静态方法addAll,用来添加多个集合的元素。*/
ArrayList<String> list1 = new ArrayList<>();
ListUtil.addAll(list1,"aaa","bbb","ccc","ddd");
System.out.println(list1);
ArrayList<Integer> list2 = new ArrayList<>();
ListUtil.addAll(list2,1,2,3,4);
System.out.println(list2);
}
}
泛型接口

java
public interface Comparable<T> {
int compareTo(T other);
}
public class Student implements Comparable<Student> {
private int id;
public int compareTo(Student other) {
return Integer.compare(this.id, other.id);
}
}
1.4 类型参数命名规范(约定俗成)
虽然你可以用任意标识符(如 <A>),但业界通用如下:
| 字母 | 含义 | 示例 |
|---|---|---|
T |
Type(类型) | Box<T> |
E |
Element(元素,常用于集合) | List<E> |
K |
Key(键) | Map<K, V> |
V |
Value(值) | Map<K, V> |
N |
Number(数字) | Calculator<N extends Number> |
S, U, V |
第二、第三、第四个类型 | Pair<S, T> |
1.5 泛型的继承
通配符 ? 用于表示"未知类型",增强泛型的灵活性。
1️⃣ 无界通配符:<?>
java
public void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
// 可以传入任何 List
printList(new ArrayList<String>());
printList(new ArrayList<Integer>());
✅ 只能读取(当作
Object),不能添加元素 (除了null)
2️⃣ 上界通配符:<? extends T> ------ PECS 原则:Producer Extends
表示"T 或 T 的子类 ",用于读取数据(生产者)
❌ 不能添加元素(因为不知道具体是哪个子类)
✅ 可以安全读取为 Number
3️⃣ 下界通配符:<? super T> ------ PECS 原则:Consumer Super
表示"T 或 T 的父类 ",用于写入数据(消费者)
java
package com.lkbhua.Test.泛型类test;
import java.util.ArrayList;
public class demo2 {
public static void main(String[] args) {
/* 需求:
定义一个方法,形参是一个集合,但是集合中的数据类型是不确定的
*/
// 创建集合的对象
ArrayList<Ye> list = new ArrayList<>();
ArrayList<Fu> list2 = new ArrayList<>();
ArrayList<Zi> list3 = new ArrayList<>();
show(list);
show(list2);
show(list3);
}
// 不确定什么类型可以使用泛型
// 但是有弊端:
// 此时他虽然可以接受任意的数据类型,
// 但是有的时候我们只希望传递部分的数据类型
// 比如:Ye 、Fu 、Zi,不愿意Student或者其他类
// 难道写Ye? 但是泛型不具备继承性,意味着你只能用Ye,传递Ye的集合类型才能使用该方法
// 泛型的通配符:?
// ? 也表示不确定的类型
// 它可以进行类型的限定
// ? extends E:表示可以传递E或者E所有的子类类型
// ? super E: 表示可以传递E或者E所有的父类类型
// 表示可以传递Fu或者Fu所有的父类类型
public static void show2(ArrayList<? super Fu> c) {
}
// 表示可以传递Ye或者Ye所有的子类类型
public static void show1(ArrayList<? extends Ye> c) {
}
public static<E> void show(ArrayList<E> c) {
}
}
// 应用场景:
// 1.集合中的数据类型不确定,但是集合中的数据类型是相同的
// 2.集合中的数据类型不确定,但是集合中的数据类型是继承关系
1.6 练习

继承关系比较简单,这里只给出测试代码:
java
package com.lkbhua.Test.泛型类test.Text;
public abstract class Animal {
private String name;
private int age;
public Animal() {}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public abstract void eat();
}
java
package com.lkbhua.Test.泛型类test.Text;
import java.util.ArrayList;
public class test {
public static void main(String[] args) {
/* 测试类中定义一个方法用于饲养动物:
public static void keepPet(ArrayList<???> list){
// 遍历集合,调用动物eat方法
}
需求1:该方法能养所有品种的猫,但是不能养狗
需求2:该方法能养所有品种的狗,但是不能养猫
需求3:该方法能养所有品种的猫和狗,但是不能传递其他的类型
*/
ArrayList<LiHuaCat> list1 = new ArrayList<>();
ArrayList<PersianCat> list2 = new ArrayList<>();
ArrayList<HuskyDog> list3 = new ArrayList<>();
ArrayList<Teddy> list4 = new ArrayList<>();
// 需求测试1
keepPet(list1);
keepPet(list2);
// keepPet(list3);
// keepPet(list4);
// 需求测试2
keepPet1(list3);
keepPet1(list4);
// keepPet1(list1);
// keepPet1(list2);
// 需求测试3
keepPet2(list1);
keepPet2(list2);
keepPet2(list3);
keepPet2(list4);
}
public static void keepPet(ArrayList<? extends Cat> list) {
}
public static void keepPet1(ArrayList<? extends Dog> list) {
}
public static void keepPet2(ArrayList<? extends Animal> list) {
}
}


声明:
分析借鉴于通义AI
以上均来源于B站@ITheima的教学内容!!!
本人跟着视频内容学习,整理知识引用