Java基础——集合进阶5
一、泛型
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>代表不确定是什么泛型,只用到别人调用了你写的泛型类才能确定。

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];}// 打印对象的时候,出现的不是地址值,而是属性值@Overridepublic String toString() {// 把数组里面所有的元素都转换成字符串拼接进行返回return Arrays.toString(obj);}
}
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);}
}
泛型方法




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);}
}
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);}
}
泛型接口

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️⃣ 无界通配符:<?>
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 的父类”,用于写入数据(消费者)
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 练习

继承关系比较简单,这里只给出测试代码:
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();
}
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<>();// 需求测试1keepPet(list1);keepPet(list2);// keepPet(list3);// keepPet(list4);// 需求测试2keepPet1(list3);keepPet1(list4);// keepPet1(list1);// keepPet1(list2);// 需求测试3keepPet2(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的教学内容!!!
本人跟着视频内容学习,整理知识引用
