# 怎么使用Java泛型
## 一、泛型概述
### 1.1 什么是泛型
泛型(Generics)是Java SE 5.0引入的重要特性,它允許在定義類、接口和方法時使用類型參數(type parameters)。這種參數化類型的能力讓代碼可以應用于多種數據類型,同時保持編譯時的類型安全。
```java
// 沒有泛型的例子
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); // 需要強制類型轉換
// 使用泛型的例子
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // 自動類型推斷
在類名后面添加類型參數聲明,可以有一個或多個類型參數。
public class Box<T> {
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
// 使用示例
Box<Integer> integerBox = new Box<>();
integerBox.set(10);
Integer value = integerBox.get();
接口也可以使用泛型,實現類需要指定具體類型或保持泛型。
public interface Pair<K, V> {
K getKey();
V getValue();
}
public class OrderedPair<K, V> implements Pair<K, V> {
private K key;
private V value;
// 實現方法...
}
在方法返回類型前聲明類型參數,可以用于靜態和非靜態方法。
public class Util {
public static <T> T getMiddle(T... a) {
return a[a.length / 2];
}
}
// 使用示例
String middle = Util.<String>getMiddle("John", "Q.", "Public");
// 類型推斷
Number middleNum = Util.getMiddle(3.14, 1729, 0);
按照Java慣例,類型參數名稱使用單個大寫字母: - E - Element(集合元素) - K - Key(鍵) - V - Value(值) - T - Type(類型) - S,U,V - 第二、第三、第四類型
使用問號(?)表示未知類型,常用于方法參數。
public static void printList(List<?> list) {
for (Object elem : list) {
System.out.println(elem);
}
}
可以限制通配符的上界或下界。
上界通配符:<? extends T> 表示T或其子類
public static double sumOfList(List<? extends Number> list) {
double sum = 0.0;
for (Number num : list) {
sum += num.doubleValue();
}
return sum;
}
下界通配符:<? super T> 表示T或其父類
public static void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 10; i++) {
list.add(i);
}
}
Java泛型是通過類型擦除實現的,編譯器在編譯時去掉類型參數信息。
// 編譯前
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0);
// 編譯后(近似)
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);
public static <E> void append(List<E> list) {
E elem = new E(); // 編譯錯誤
list.add(elem);
}
List<Integer>[] arrayOfLists = new List<Integer>[2]; // 編譯錯誤
靜態字段或方法不能引用類的類型參數。
public class MobileDevice<T> {
private static T os; // 編譯錯誤
public static T getOS() { // 編譯錯誤
return os;
}
}
// 編譯錯誤
try {
// ...
} catch (SomeException<Integer> e) {
// ...
}
Producer-Extends, Consumer-Super(生產者使用extends,消費者使用super)
// 正確示例
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dest.set(i, src.get(i));
}
}
當方法操作獨立于類類型參數時,使用泛型方法更靈活。
// 優于在類上定義類型參數
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
List<T> result = new ArrayList<>();
for (T t : list) {
if (p.test(t)) {
result.add(t);
}
}
return result;
}
總是使用泛型類型,避免使用原生類型(raw type)。
List list = new ArrayList(); // 避免
List<String> list = new ArrayList<>(); // 推薦
public class Cache<K, V> {
private Map<K, V> cacheMap = new HashMap<>();
public void put(K key, V value) {
cacheMap.put(key, value);
}
public V get(K key) {
return cacheMap.get(key);
}
public void remove(K key) {
cacheMap.remove(key);
}
}
public class CompareUtil {
public static <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
public static <T extends Comparable<? super T>> void sort(List<T> list) {
Collections.sort(list);
}
}
不能直接使用,必須使用對應的包裝類。Java的泛型是基于對象的,基本類型不是對象。
由于類型擦除,運行時無法直接檢查泛型類型??梢酝ㄟ^傳遞Class對象來解決:
public <T> void checkType(Object obj, Class<T> type) {
if (type.isInstance(obj)) {
T t = type.cast(obj);
// 處理t
}
}
子類可以繼承或擴展父類的泛型參數:
class Parent<T> {}
class Child<T> extends Parent<T> {}
class StringChild extends Parent<String> {}
Java泛型是強大的語言特性,正確使用可以: 1. 提高代碼的類型安全性 2. 減少類型轉換的繁瑣 3. 增強代碼的可讀性和重用性 4. 使API設計更加靈活
雖然泛型有一些限制(主要是由于類型擦除),但通過合理的設計模式和使用技巧,可以充分發揮其優勢。掌握泛型是成為高級Java開發者的必備技能。
本文涵蓋了Java泛型的主要知識點,從基礎概念到高級應用,共約3700字。通過示例代碼和最佳實踐,幫助讀者全面理解并正確使用Java泛型。在實際開發中,應根據具體場景靈活運用泛型特性,編寫出更安全、更靈活的代碼。 “`
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。