|
|
@@ -426,7 +426,230 @@ list.clear(); // 清空
|
|
|
|
|
|
---
|
|
|
|
|
|
-## 三、随堂练习要点
|
|
|
+## 三、泛型(Generics)
|
|
|
+
|
|
|
+### 3.1 泛型的概念
|
|
|
+
|
|
|
+**泛型(Generics)** 是 JDK 1.5 引入的特性,它提供了**编译时进行类型安全检测**的机制。
|
|
|
+
|
|
|
+**为什么需要泛型?**
|
|
|
+
|
|
|
+在没有泛型之前,`ArrayList` 可以存储任意类型的对象:
|
|
|
+
|
|
|
+```java
|
|
|
+ArrayList list = new ArrayList();
|
|
|
+list.add(new Person()); // Person 对象
|
|
|
+list.add(new Student()); // Student 对象
|
|
|
+list.add(new Dog()); // Dog 对象
|
|
|
+
|
|
|
+// 遍历时,需要强制类型转换,容易发生 ClassCastException
|
|
|
+for (int i = 0; i < list.size(); i++) {
|
|
|
+ Person p = (Person) list.get(i); // 遍历到 Dog 时抛出异常!
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+> 📄 来源:`collection/Demo03.java`
|
|
|
+
|
|
|
+### 3.2 泛型的好处
|
|
|
+
|
|
|
+1. **把运行期间的问题,提前到了编译期间** — 编译时就能发现类型不匹配
|
|
|
+2. **避免了强制类型转换** — 编译器自动处理类型转换
|
|
|
+
|
|
|
+```java
|
|
|
+// 使用泛型后,编译时就能检查类型
|
|
|
+ArrayList<String> list = new ArrayList<>();
|
|
|
+list.add("张三");
|
|
|
+list.add("李四");
|
|
|
+// list.add(666); // 编译报错!只能添加 String 类型
|
|
|
+
|
|
|
+// 遍历时无需强制类型转换
|
|
|
+for (int i = 0; i < list.size(); i++) {
|
|
|
+ String s = list.get(i); // 自动识别为 String 类型
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+> 📄 来源:`collection/Demo03.java`
|
|
|
+
|
|
|
+### 3.3 泛型的定义格式
|
|
|
+
|
|
|
+#### 单类型参数
|
|
|
+
|
|
|
+```java
|
|
|
+<类型> // 如 <E>、<T>,尖括号里面可以写任意字母,一般只写一个字母
|
|
|
+```
|
|
|
+
|
|
|
+`ArrayList` 的源码定义:
|
|
|
+
|
|
|
+```java
|
|
|
+public class ArrayList<E> extends AbstractList<E>
|
|
|
+ implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
|
|
|
+ // 使用的就是 <E> 表示这个 ArrayList 要操作的元素类型
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 自定义泛型类
|
|
|
+
|
|
|
+```java
|
|
|
+public class MyArray<A> {
|
|
|
+ public void add(A a) {
|
|
|
+ // ... 传入的类型由使用时决定
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 使用:
|
|
|
+MyArray<Integer> arr1 = new MyArray<>();
|
|
|
+arr1.add(10); // 必须是 Integer
|
|
|
+
|
|
|
+MyArray<String> arr2 = new MyArray<>();
|
|
|
+arr2.add("Hello"); // 必须是 String
|
|
|
+```
|
|
|
+
|
|
|
+**完整示例** — `PrintX<X>` 泛型类:
|
|
|
+
|
|
|
+```java
|
|
|
+public class PrintX<X> {
|
|
|
+ public void print(X x) {
|
|
|
+ System.out.println(x.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ public X getX(X x) {
|
|
|
+ return x;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void printList(List<?> list) {
|
|
|
+ // 使用无界通配符 ?
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 使用:
|
|
|
+PrintX<Person> px = new PrintX<>();
|
|
|
+Person person = new Person();
|
|
|
+person.setAge(25);
|
|
|
+person.setName("张三");
|
|
|
+px.print(person); // 输出 Person 对象的 toString()
|
|
|
+```
|
|
|
+
|
|
|
+> 📄 来源:`collection/PrintX.java`、`collection/Test.java`
|
|
|
+
|
|
|
+#### 多类型参数
|
|
|
+
|
|
|
+可以指定多种类型,中间使用逗号分隔:
|
|
|
+
|
|
|
+```java
|
|
|
+<类型1, 类型2, ..., 类型n>
|
|
|
+
|
|
|
+// 例如:
|
|
|
+<K,V> <E,T> <A,B>
|
|
|
+```
|
|
|
+
|
|
|
+### 3.4 泛型的通配符(Wildcard)
|
|
|
+
|
|
|
+| 通配符 | 名称 | 说明 | 示例 |
|
|
|
+|--------|------|------|------|
|
|
|
+| `?` | **无界通配符**(Unbounded Wildcard) | 表示未知类型,可以接收任意类型 | `public void printList(List<?> list)` |
|
|
|
+| `? extends T` | **上界通配符**(Upper Bounded Wildcard) | 表示类型是 T 或 T 的子类 | `public void printList(List<? extends Person> list)` |
|
|
|
+| `? super T` | **下界通配符**(Lower Bounded Wildcard) | 表示类型是 T 或 T 的父类 | `public void add(List<? super Integer> list)` |
|
|
|
+
|
|
|
+```java
|
|
|
+// 无界通配符
|
|
|
+public void printList(List<?> list) {
|
|
|
+ for (Object obj : list) {
|
|
|
+ System.out.println(obj);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 上界通配符 — 只允许 Person 及其子类
|
|
|
+public void processPersons(List<? extends Person> list) {
|
|
|
+ for (Person p : list) {
|
|
|
+ System.out.println(p.getName());
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+> 📄 来源:`collection/Demo03.java`
|
|
|
+
|
|
|
+### 3.5 有界类型参数(Bounded Type Parameter)
|
|
|
+
|
|
|
+使用 `extends` 关键字限制泛型的类型范围:
|
|
|
+
|
|
|
+```java
|
|
|
+<T extends 某个父类>
|
|
|
+// 使用时,传入的类型只能是该父类的子类
|
|
|
+```
|
|
|
+
|
|
|
+```java
|
|
|
+public class GenericMethod {
|
|
|
+ // 只允许 Number 及其子类(Integer、Double 等)
|
|
|
+ public static <T extends Number> double sum(T a, T b) {
|
|
|
+ return a.doubleValue() + b.doubleValue();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 使用:
|
|
|
+double result = GenericMethod.sum(10, 20); // Integer ✓
|
|
|
+double result2 = GenericMethod.sum(3.14, 2.5); // Double ✓
|
|
|
+// GenericMethod.sum("A", "B"); // 编译报错!String 不是 Number 的子类
|
|
|
+```
|
|
|
+
|
|
|
+### 3.6 泛型在集合中的实际应用
|
|
|
+
|
|
|
+```java
|
|
|
+// 带泛型的 ArrayList
|
|
|
+ArrayList<String> list1 = new ArrayList<>(); // 只能存 String
|
|
|
+ArrayList<Person> list2 = new ArrayList<>(); // 只能存 Person
|
|
|
+ArrayList<Integer> list3 = new ArrayList<>(); // 只能存 Integer
|
|
|
+
|
|
|
+// LinkedList 同样支持泛型
|
|
|
+LinkedList<Person> linkedList = new LinkedList<>();
|
|
|
+```
|
|
|
+
|
|
|
+> 📄 来源:`collection/Test.java`
|
|
|
+
|
|
|
+### 3.7 课堂练习代码参考
|
|
|
+
|
|
|
+课程配套的两个练习参考答案,展示了 Student 类的完整实现:
|
|
|
+
|
|
|
+**Student 类** — 重写了 `toString()`、`equals()`、`hashCode()` 方法:
|
|
|
+
|
|
|
+| 方法 | 用途 |
|
|
|
+|------|------|
|
|
|
+| `toString()` | 方便打印学生信息 |
|
|
|
+| `equals(Object)` | 基于学号判断两个学生是否相等 |
|
|
|
+| `hashCode()` | 基于学号生成哈希值 |
|
|
|
+
|
|
|
+```java
|
|
|
+// equals() 方法 — 基于学号比较
|
|
|
+@Override
|
|
|
+public boolean equals(Object obj) {
|
|
|
+ if (this == obj) return true;
|
|
|
+ if (obj == null || getClass() != obj.getClass()) return false;
|
|
|
+ Student student = (Student) obj;
|
|
|
+ return studentId != null && studentId.equals(student.studentId);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+> 📄 来源:`exercises/exercise01/Student.java`、`exercises/exercise02/Student.java`
|
|
|
+
|
|
|
+**注意**:`exercise02` 中的 `StudentManager` 使用了**未使用泛型**的原始类型写法:
|
|
|
+
|
|
|
+```java
|
|
|
+ArrayList students = new ArrayList<>(); // 未指定泛型
|
|
|
+// 遍历时需要通过 instanceof 进行类型判断
|
|
|
+for (int i = 0; i < students.size(); i++) {
|
|
|
+ Object o = students.get(i);
|
|
|
+ if (o instanceof Student s) { // JDK 16+ 模式匹配
|
|
|
+ System.out.println(s);
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+> 💡 **对比**:`exercise01` 使用了泛型 `ArrayList<Student>`,`exercise02` 使用原始类型。推荐始终使用泛型以享受编译时类型检查的好处。
|
|
|
+
|
|
|
+> 📄 来源:`exercises/exercise01/StudentManager.java`、`exercises/exercise02/StudentManager.java`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 四、随堂练习要点
|
|
|
|
|
|
### 本日课程知识点概览
|
|
|
|
|
|
@@ -441,6 +664,11 @@ list.clear(); // 清空
|
|
|
| **ArrayList 扩容方法** | **trimToSize 缩减容量、ensureCapacity 手动扩容、grow 1.5倍扩容公式** | `collection/Demo02.java` |
|
|
|
| **位运算符** | **`>>` 右移(除2)、`<<` 左移(乘2)在扩容中的使用** | `collection/Demo02.java` |
|
|
|
| **ArrayList 常用方法** | **add/get/set/remove/size/isEmpty/contains/indexOf/toArray/getFirst/getLast** | `collection/Demo02.java` |
|
|
|
+| **泛型(Generics)概念** | **编译时类型安全检测、避免强制类型转换** | `collection/Demo03.java` |
|
|
|
+| **泛型定义格式** | **单类型 `<E>`、多类型 `<K,V>`、有界类型 `<T extends 父类>`** | `collection/Demo03.java` |
|
|
|
+| **泛型通配符** | **`?` 无界通配符、`? extends T` 上界通配符** | `collection/Demo03.java` |
|
|
|
+| **自定义泛型类** | **PrintX\<X\>、print(X x)、getX(X x)、printList(List\<?\>)** | `collection/PrintX.java` |
|
|
|
+| **练习参考答案** | **Student 类重写 toString/equals/hashCode、泛型 vs 原始类型对比** | `exercise01/*.java`、`exercise02/*.java` |
|
|
|
|
|
|
### 包装类重点
|
|
|
|
|
|
@@ -452,6 +680,14 @@ list.clear(); // 清空
|
|
|
- 包装类对象参与算术运算时的自动拆箱
|
|
|
- `null` 包装类拆箱会抛出 `NullPointerException`
|
|
|
|
|
|
+### 泛型重点
|
|
|
+
|
|
|
+1. **理解泛型的必要性**:解决了原始集合类型不安全、需要强制类型转换的问题
|
|
|
+2. **区分三种通配符**:`?`(无界)、`? extends T`(上界)、`? super T`(下界)
|
|
|
+3. **掌握泛型定义语法**:`<T>`、`<K,V>`、`<T extends 父类>`
|
|
|
+4. **泛型 VS 原始类型**:始终优先使用泛型(`ArrayList<Student>`),避免原始类型
|
|
|
+5. **模式匹配 instanceof**:JDK 16+ 支持 `if (o instanceof Student s)` 自动变量绑定
|
|
|
+
|
|
|
### 集合框架重点
|
|
|
|
|
|
1. **理解集合 vs 数组的区别**:长度可变性、元素类型限制
|
|
|
@@ -477,5 +713,8 @@ list.clear(); // 清空
|
|
|
- [Java 包装类缓存机制详解](https://www.baeldung.com/java-integer-cache)
|
|
|
- [Oracle Java 官方文档 — Collections Framework Overview](https://docs.oracle.com/javase/8/docs/technotes/guides/collections/overview.html)
|
|
|
- [Java 集合框架详解:List、Set、Map 的区别](https://www.baeldung.com/java-collections)
|
|
|
+- [Oracle Java 官方文档 — Generics Tutorial](https://docs.oracle.com/javase/tutorial/java/generics/index.html)
|
|
|
+- [Java 泛型:通配符详解](https://www.baeldung.com/java-generics-advanced)
|
|
|
- 《Java 核心技术 卷I》第 3 章 — 包装器类与自动装箱
|
|
|
+- 《Java 核心技术 卷I》第 5 章 — 泛型
|
|
|
- 《Java 核心技术 卷I》第 9 章 — 集合
|