目錄
一、概述
(一)定義
(二)作用
(三)引入原因
二、使用
(一)類
(二)接口
(三)方法
三、類型參數
(一)命名
(二)限制
四、類型擦除
(一)概念
(二)影響
五、通配符
(一)`?`(無界通配符)
(二)`? extends T`(上界通配符)
(三)`? super T`(下界通配符)
六、高級用法
(一)泛型數組
(二)泛型與反射
(三)泛型與匿名內部類
七、idea項目實戰
(一)打開idea新建Java項目
(二)編寫GenericCollectionUtil類
(三)創建測試類
(四)編譯運行代碼,注意切換到測試類文件
(五)運行結果如下
(六)代碼解釋如下
1. GenericCollectionUtil?類
2. GenericCollectionUtilTest`類
一、概述
(一)定義
? ? ? ? Java泛型(Generics)是指在定義類、接口或方法時,可以指定一個或多個類型參數(Type Parameters),這些類型參數在使用時會被具體的類型替換。它允許程序員在編寫代碼時延遲確定數據類型,從而提高代碼的復用性和安全性。
(二)作用
? ? ? ?Java泛型是一種強大的特性,它可以讓代碼更加通用、安全和復用。通過泛型類、泛型接口和泛型方法,可以編寫出能夠處理多種類型數據的代碼。同時,泛型的類型參數、類型擦除和通配符等概念也需要深入理解,以便正確使用泛型。
(三)引入原因
1. 類型安全
? ?- 在沒有泛型的情況下,集合類(如ArrayList)只能存儲Object類型的對象。當從集合中取出元素時,需要進行強制類型轉換。例如:
? ? ?List list = new ArrayList();
? ? ?list.add("Hello");
? ? ?String str = (String) list.get(0);
? ? ?這種方式存在類型安全問題,因為list可以存儲任何類型的對象,如果添加了其他類型的對象,如`list.add(123);`,在取出時進行強制類型轉換就會拋出`ClassCastException`異常。
? ?- 使用泛型后,可以在編譯階段就檢查類型是否正確。例如:
? ? ?List<String> list = new ArrayList<String>();
? ? ?list.add("Hello");
? ? ?String str = list.get(0); // 不需要強制類型轉換
? ? ?如果嘗試添加非String類型的對象,如`list.add(123);`,編譯器會報錯。
2. 代碼復用
? ?- 泛型可以讓我們編寫更加通用的代碼。例如,一個泛型類可以處理多種類型的數據,而不需要為每種數據類型都編寫一個類。
二、使用
(一)類
1. 定義泛型類
? ?- 泛型類的定義格式為:
? ? ?class 類名<類型參數> {
? ? ? ? ?// 類的成員可以使用類型參數
? ? ?}
? ?- 例如,定義一個簡單的泛型類Box:
? ? ?public class Box<T> {
? ? ? ? ?private T t;
? ? ? ? ?public void set(T t) {
? ? ? ? ? ? ?this.t = t;
? ? ? ? ?}
? ? ? ? ?public T get() {
? ? ? ? ? ? ?return t;
? ? ? ? ?}
? ? ?}
2. 使用泛型類
? ?- 使用時需要指定具體的類型參數。例如:
? ? ?Box<String> stringBox = new Box<String>();
? ? ?stringBox.set("Hello");
? ? ?String str = stringBox.get();
? ? ?Box<Integer> integerBox = new Box<Integer>();
? ? ?integerBox.set(123);
? ? ?Integer num = integerBox.get();
?
(二)接口
1. 定義泛型接口
? ?- 泛型接口的定義格式與泛型類類似:
? ?? ? ?interface 接口名<類型參數> {
? ? ? ? ?// 接口的成員可以使用類型參數
? ? ?}
? ?- 例如,定義一個泛型接口Comparable:
?
? ? ?public interface Comparable<T> {
? ? ? ? ?int compareTo(T o);
? ? ?}
2. 實現泛型接口
? ?- 實現泛型接口時,可以指定具體的類型參數,也可以不指定。例如:
??
? ? ?public class Person implements Comparable<Person> {
? ? ? ? ?private String name;
? ? ? ? ?public Person(String name) {
? ? ? ? ? ? ?this.name = name;
? ? ? ? ?}
? ? ? ? ?@Override
? ? ? ? ?public int compareTo(Person o) {
? ? ? ? ? ? ?return this.name.compareTo(o.name);
? ? ? ? ?}
? ? ?}
?
(三)方法
1. 定義泛型方法
? ?- 泛型方法的定義格式為:
?
? ? ?<類型參數> 返回值類型 方法名(參數列表) {
? ? ? ? ?// 方法體
? ? ?}
? ?- 例如,定義一個泛型方法swap:
? ? ?public <T> void swap(T[] array, int i, int j) {
? ? ? ? ?T temp = array[i];
? ? ? ? ?array[i] = array[j];
? ? ? ? ?array[j] = temp;
? ? ?}
2. 使用泛型方法
? ?- 調用泛型方法時,可以省略類型參數,編譯器會自動推斷。例如:
? ? ?String[] strArray = {"a", "b"};
? ? ?swap(strArray, 0, 1);
?
三、類型參數
(一)命名
- 通常使用單個大寫字母作為類型參數的名稱,常見的有:
? - `T`:Type
? - `E`:Element
? - `K`:Key
? - `V`:Value
? - `N`:Number
(二)限制
1. 有界類型參數
? ?- 可以通過`extends`關鍵字對類型參數進行限制,使其只能是某個類或接口的子類型。例如:
? ? ?public <T extends Number> void print(T t) {
? ? ? ? ?System.out.println(t);
? ? ?}
? ? ?這樣,`T`只能是`Number`或其子類的實例。
2. 無界類型參數
? ?- 如果沒有對類型參數進行限制,那么它就是一個無界類型參數,可以是任何類型。例如:
? ? ?public <T> void print(T t) {
? ? ? ? ?System.out.println(t);
? ? ?}
?
四、類型擦除
(一)概念
- 在Java中,泛型是通過類型擦除實現的。也就是說,在運行時,泛型類型會被擦除,所有的類型參數都會被替換為它們的邊界(如果有邊界的話),如果沒有邊界,則替換為`Object`。
- 例如,`List<String>`和`List<Integer>`在運行時都會被擦除為`List`。
(二)影響
1.無法獲取泛型類型參數
? ?- 由于類型擦除,無法在運行時獲取泛型類型參數的具體類型。例如:
? ? ?List<String> list = new ArrayList<String>();
? ? ?System.out.println(list.getClass().getTypeParameters()); // 輸出的是一個空的TypeVariable數組
2.泛型方法的重載
? ?- 由于類型擦除,泛型方法的簽名在運行時是相同的,因此不能僅根據類型參數的不同來重載泛型方法。例如:
??
? ? ?public <T> void print(T t) {
? ? ? ? ?System.out.println(t);
? ? ?}
? ? ?public <E> void print(E e) {
? ? ? ? ?System.out.println(e);
? ? ?}
? ? ?這是不允許的,因為編譯器無法區分這兩個方法。
五、通配符
(一)`?`(無界通配符)
- 表示任意類型。例如:
? public void printList(List<?> list) {
? ? ? for (Object obj : list) {
? ? ? ? ? System.out.println(obj);
? ? ? }
? }
? 這個方法可以接受任何類型的`List`作為參數。
(二)`? extends T`(上界通配符)
- 表示類型參數是`T`或`T`的子類型。例如:
? public void printNumberList(List<? extends Number> list) {
? ? ? for (Number num : list) {
? ? ? ? ? System.out.println(num);
? ? ? }
? }
? 這個方法可以接受`List<Number>`、`List<Integer>`、`List<Double>`等作為參數。
(三)`? super T`(下界通配符)
- 表示類型參數是`T`或`T`的父類型。例如:
? public void addNumberToList(List<? super Integer> list) {
? ? ? list.add(123);
? }
? 這個方法可以接受`List<Integer>`、`List<Number>`、`List<Object>`等作為參數。
六、高級用法
(一)泛型數組
- 不能創建泛型數組,例如`new T[10]`是不允許的。但是可以通過其他方式來實現類似的功能。例如:
? public <T> T[] createArray(Class<T> clazz, int size) {
? ? ? return (T[]) Array.newInstance(clazz, size);
? }
?
(二)泛型與反射
- 可以通過反射來獲取泛型類型參數。例如:
?
? Type type = new TypeReference<List<String>>() {}.getType();
? if (type instanceof ParameterizedType) {
? ? ? ParameterizedType parameterizedType = (ParameterizedType) type;
? ? ? Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
? ? ? System.out.println(actualTypeArguments[0]); // 輸出:class java.lang.String
? }
?
(三)泛型與匿名內部類
- 可以在匿名內部類中使用泛型。例如:
? public static void main(String[] args) {
? ? ? List<String> list = new ArrayList<String>() {
? ? ? ? ? {
? ? ? ? ? ? ? add("Hello");
? ? ? ? ? }
? ? ? };
? ? ? System.out.println(list.get(0));
? }
?
七、idea項目實戰
(一)打開idea新建Java項目
ps:如果需要,可以在pom.xml
文件中添加其他依賴。對于這個簡單的項目不需要額外的依賴。
(二)編寫GenericCollectionUtil
類
package com.example.util;import java.util.ArrayList;
import java.util.List;public class GenericCollectionUtil<T> {private List<T> list = new ArrayList<>();/*** 添加元素到集合** @param element 要添加的元素*/public void add(T element) {list.add(element);}/*** 獲取集合中的元素** @param index 元素的索引* @return 索引對應的元素*/public T get(int index) {return list.get(index);}/*** 清空集合*/public void clear() {list.clear();}/*** 獲取集合大小** @return 集合的大小*/public int size() {return list.size();}@Overridepublic String toString() {return list.toString();}
}
(三)創建測試類
package com.example.util;public class GenericCollectionUtilTest {public static void main(String[] args) {// 測試字符串集合GenericCollectionUtil<String> stringUtil = new GenericCollectionUtil<>();stringUtil.add("Hello");stringUtil.add("World");System.out.println("String Collection: " + stringUtil);System.out.println("Element at index 0: " + stringUtil.get(0));stringUtil.clear();System.out.println("After clear: " + stringUtil);// 測試整數集合GenericCollectionUtil<Integer> intUtil = new GenericCollectionUtil<>();intUtil.add(1);intUtil.add(2);intUtil.add(3);System.out.println("Integer Collection: " + intUtil);System.out.println("Element at index 1: " + intUtil.get(1));intUtil.clear();System.out.println("After clear: " + intUtil);}
}
(四)編譯運行代碼,注意切換到測試類文件
ps:紅色箭頭所示兩種點擊都可以實現
(五)運行結果如下
(六)代碼解釋如下
1. GenericCollectionUtil?類
? ? ? ?這個類是一個泛型工具類,用于處理不同類型的數據集合操作。它使用了 Java 的泛型特性,使得同一個類可以處理多種類型的數據。
成員變量
- `private List<T> list = new ArrayList<>();`
? - 這是一個泛型列表,用于存儲集合中的元素。`T` 是一個類型參數,表示集合中元素的類型。
方法
- `public void add(T element)`
? - 這是一個泛型方法,用于向集合中添加元素。`element` 是要添加的元素,其類型由 `T` 決定。
??
- `public T get(int index)`
? - 這是一個泛型方法,用于從集合中獲取指定索引位置的元素。返回值的類型也是 `T`。
??
- `public void clear()`
? - 這是一個非泛型方法,用于清空集合中的所有元素。
??
- `public int size()`
? - 這是一個非泛型方法,用于獲取集合中元素的數量。
??
- `@Override public String toString()`
? - 這是一個重寫的方法,用于返回集合的字符串表示形式。它直接返回 `list.toString()`,即集合中所有元素的字符串表示。
2. GenericCollectionUtilTest`類
這個類是一個測試類,用于驗證 `GenericCollectionUtil` 類的功能。
主要邏輯
- 創建 `GenericCollectionUtil` 類的實例,分別用于字符串和整數類型的集合。
- 向集合中添加元素,并打印集合的字符串表示形式。
- 獲取并打印指定索引位置的元素。
- 清空集合,并打印清空后的集合表示形式。
輸出示例
- 打印字符串集合和整數集合的初始狀態。
- 打印指定索引位置的元素。
- 打印清空集合后的狀態。
優點
- **泛型使用**:通過使用泛型,代碼更加通用和靈活,可以處理多種類型的數據集合。
- **簡潔性**:代碼簡潔明了,易于理解和維護。
- **功能明確**:每個方法的功能都很明確,易于使用。
缺點
- **功能有限**:目前只實現了添加元素、獲取元素、清空集合和獲取集合大小的功能,還可以擴展更多功能,如刪除元素、查找元素等。
- **異常處理**:在獲取元素時,如果索引超出范圍,會拋出 `IndexOutOfBoundsException` 異常。可以考慮添加異常處理邏輯,提高代碼的健壯性。
擴展功能建議
- **刪除元素**:添加一個方法,用于從集合中刪除指定索引位置的元素。
- **查找元素**:添加一個方法,用于查找集合中是否存在指定的元素。
- **排序**:添加一個方法,用于對集合中的元素進行排序。
- **過濾**:添加一個方法,用于對集合中的元素進行過濾,返回滿足條件的元素集合。