文章目錄
- 泛型類
- 泛型方法
- 泛型類派生子類
- 示例 1:子類固定父類泛型類型(`StringBox` 繼承自 `Box<String>`)
- 示例 2:子類保留父類泛型類型(`AdvancedBox<T>` 繼承自 `Box<T>`)
- 示例 3:添加子類自己的泛型參數(`KeyValuePair<K,V>` 繼承自 `Pair<K>`)
- 示例 4:約束父類類型邊界(`IntCalculator` 繼承自 `NumberCalculator<Integer>`)
- 類型通配符
- 示例 1:無界通配符(`<?>`)
- 示例 2:上界通配符(`<? extends Number>`)
- 示例 3:下界通配符(`<? super Integer>`)
泛型類
泛 型 類 就 是 把 泛 型 定 義 在 類 上 , 用 戶 使 用 該 類 的 時 候 , 才 把 類 型 明 確 下 來 。 這樣的話, 用戶明確了什么類型 , 該類就代表著什么類型 , 用戶在使用的時候就不用擔心強轉的問題, 和運行時轉換異常的問題了。
public class Box<T> {private T content; // 泛型字段public void setContent(T content) {this.content = content;}public T getContent() {return content;}public static void main(String[] args) {// 存儲字符串類型Box<String> stringBox = new Box<>();stringBox.setContent("Hello Generics");System.out.println(stringBox.getContent()); // 輸出: Hello Generics// 存儲整數類型Box<Integer> intBox = new Box<>();intBox.setContent(100);int value = intBox.getContent(); // 無需強制類型轉換System.out.println(value); // 輸出: 100}
}
關鍵點:
Box<T>
的 T 是類型參數,使用時由開發者指定具體類型(如 String 或 Integer)。- 類型安全:例如 stringBox.setContent(100) 會在編譯時報錯。
泛型方法
除了在類上使用泛型 ,我們可能就 僅 僅 在 某 個 方 法 上 需 要 使 用 泛 型 , 外界僅僅是關心該方法 , 不關心類其他的屬性 , 這樣的話 , 我們在整個類上定義泛型 , 未免就有些大題小作了 。 那么此時 , 我們可以采用泛型方法。
public class MaxFinder {/*** 泛型方法:找到數組中的最大值(要求元素類型實現 Comparable 接口)* @param array 輸入數組* @return 最大值*/public static <T extends Comparable<T>> T findMax(T[] array) {if (array == null || array.length == 0) {throw new IllegalArgumentException("數組不能為空");}T max = array[0];for (T item : array) {if (item.compareTo(max) > 0) {max = item;}}return max;}public static void main(String[] args) {Integer[] integers = {5, 8, 2, 10};Integer maxInt = findMax(integers); // 調用時類型為 IntegerSystem.out.println("Max Integer: " + maxInt); // 輸出: 10String[] fruits = {"Apple", "Banana", "Mango"};String maxFruit = findMax(fruits); // 調用時類型為 StringSystem.out.println("Max String: " + maxFruit); // 輸出: Mango(按字典序比較)}
}
關鍵點:
<T>
聲明在方法返回類型前,表示這是一個泛型方法,獨立于類是否泛型。<T extends Comparable<T>>
確保類型 T 的對象可以相互比較(通過 compareTo 方法)。- 類型安全:拒絕不可比較的類型:例如 findMax(new Object[]{…}) 會編譯失敗。
- 返回
T
類型,確保返回元素類型與輸入數組類型完全一致。
泛型類派生子類
前 面 我 們 已 經 定 義 了 泛型 類 , 泛 型 類 是 擁有泛型特性的類 , 它本質上還 是 一 個 J av a 類 , 那 么 它 就 可 以 被 繼 承 或 實 現 。這個情況比較多:
示例 1:子類固定父類泛型類型(StringBox
繼承自 Box<String>
)
子類直接指定父類泛型參數的具體類型,適用于特定場景的擴展:
// 泛型父類
class Box<T> {private T content;public void setContent(T content) {this.content = content;}public T getContent() {return content;}
}
// 子類繼承時指定父類泛型類型為 String
class StringBox extends Box<String> {// 新增方法:專為字符串內容設計public void toUpperCase() {String value = getContent(); // 直接使用 String 類型if (value != null) { setContent(value.toUpperCase());}}
}
public class Main {public static void main(String[] args) {StringBox box = new StringBox();box.setContent("hello");box.toUpperCase();System.out.println(box.getContent()); // 輸出: HELLO}
}
關鍵點:
- 子類 StringBox 繼承時固定了父類泛型為 String,因此 getContent() 直接返回 String 類型。
- 子類可以添加與固定類型相關的專屬方法(如 toUpperCase)。
示例 2:子類保留父類泛型類型(AdvancedBox<T>
繼承自 Box<T>
)
子類保持父類泛型參數的靈活性,并擴展新功能:
// 父類定義不變,與示例1相同// 子類保留父類的泛型參數 <T>
class AdvancedBox<T> extends Box<T> {// 新增方法:檢查內容是否為空public boolean isEmpty() {return getContent() == null;}// 新增方法:重置內容為 nullpublic void clear() {setContent(null);}
}public class Main2 {public static void main(String[] args) {AdvancedBox<Integer> intBox = new AdvancedBox<>();intBox.setContent(100);System.out.println(intBox.isEmpty()); // 輸出: falseintBox.clear();System.out.println(intBox.getContent()); // 輸出: null}
}
關鍵點:
- 子類
AdvancedBox<T>
保留父類泛型參數<T>
,可復用父類邏輯。 - 功能上擴展了新方法(如 isEmpty 和 clear),獨立于具體類型的通用操作。
示例 3:添加子類自己的泛型參數(KeyValuePair<K,V>
繼承自 Pair<K>
)
父子類均使用泛型,子類引入新的類型參數:
// 泛型父類
class Pair<T> {private T first;private T second;public Pair(T first, T second) {this.first = first;this.second = second;}public T getFirst() { return first; }public T getSecond() { return second; }
}// 子類添加新的泛型參數 V,與父類參數 K 獨立
class KeyValuePair<K, V> extends Pair<K> {private V value;public KeyValuePair(K key, V value) {super(key, key); // 父類需要兩個同類型參數,此處復用 key 作為示例this.value = value;}// 新增方法:獲取鍵值對的獨立值public V getValue() { return value; }
}public class Main3 {public static void main(String[] args) {KeyValuePair<String, Integer> entry = new KeyValuePair<>("Age", 30);String key = entry.getFirst(); // 類型為 String(父類返回值)int value = entry.getValue(); // 類型為 Integer(子類新增方法)System.out.println(key + ": " + value); // 輸出: Age: 30}
}
關鍵點:
- 子類
KeyValuePair<K, V>
擴展了父類的泛型參數<K>
,并新增了獨立參數<V>
。 - 父子類泛型參數獨立,子類可以實現更復雜的數據結構(如鍵值對)。
示例 4:約束父類類型邊界(IntCalculator
繼承自 NumberCalculator<Integer>
)
子類繼承時遵循父類的泛型約束(如 <T extends Number>
),并進一步具體化:
// 泛型父類,約束類型必須為 Number 的子類
class NumberCalculator<T extends Number> {protected T value;public NumberCalculator(T value) {this.value = value;}public double doubleValue() {return value.doubleValue();}
}// 子類指定父類泛型類型為 Integer
class IntCalculator extends NumberCalculator<Integer> {public IntCalculator(Integer value) {super(value);}// 新增方法:計算平方(專為 Integer 設計)public int squareInt() {return value * value;}
}public class Main4 {public static void main(String[] args) {IntCalculator calc = new IntCalculator(5);System.out.println(calc.doubleValue()); // 輸出: 5.0(調用父類方法)System.out.println(calc.squareInt()); // 輸出: 25(子類專屬方法)}
}
關鍵點:
- 父類
NumberCalculator<T>
的泛型參數已被約束為<T extends Number>
。 - 子類
IntCalculator
繼承時將泛型固定為Integer
,確保父類邏輯安全,同時添加Integer
專用的新方法。
類型通配符
示例 1:無界通配符(<?>
)
用于處理未知類型的集合,適合只讀取集合內容的場景:
public class UnboundedWildcardDemo {/*** 打印任意類型集合的元素* @param list 使用無界通配符 <?>,表示接受任意類型的 List*/public static void printList(List<?> list) {for (Object item : list) {System.out.print(item + " ");}System.out.println();}public static void main(String[] args) {List<String> names = Arrays.asList("Alice", "Bob", "Charlie");List<Integer> numbers = Arrays.asList(1, 2, 3);printList(names); // 輸出: Alice Bob Charlie printList(numbers); // 輸出: 1 2 3 }
}
關鍵點:
List<?>
可以接受任何類型的 List,如List<String>
、List<Integer>
。- 只能讀取元素(返回 Object),但 不能添加 元素(除了 null),因為具體類型未知。
示例 2:上界通配符(<? extends Number>
)
限制類型為 Number 或其子類(如 Integer、Double),適合讀取場景:
public class UpperBoundedWildcardDemo {/*** 計算數值列表的總和* @param list 使用上界通配符 <? extends Number>* @return Sum of numbers as double*/public static double sumOfList(List<? extends Number> list) {double sum = 0.0;for (Number num : list) {sum += num.doubleValue(); // 調用 Number 的方法}return sum;}public static void main(String[] args) {List<Integer> integers = Arrays.asList(1, 2, 3);List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);System.out.println(sumOfList(integers)); // 輸出: 6.0System.out.println(sumOfList(doubles)); // 輸出: 6.6}
}
關鍵點:
- 參數 list 可以接受
List<Integer>
、List<Double>
等(只要元素是Number
的子類)。 - 不能添加元素(如 list.add(5) 會編譯報錯),具體子類型未知,可能破壞類型安全。
示例 3:下界通配符(<? super Integer>
)
限制類型為 Integer 或其父類(如 Number、Object),適合寫入場景:
public class LowerBoundedWildcardDemo {/*** 向集合中添加多個 Integer 值* @param dest 使用下界通配符 <? super Integer>,可以接受Integer或其父類(如Number、Object)的List* @param values 要添加的多個 Integer 值*/public static void addNumbersToList(List<? super Integer> dest, List<Integer> values) {dest.addAll(values); }public static void main(String[] args) {List<Number> numberList = new ArrayList<>();addNumbersToList(numberList, Arrays.asList(10, 20, 30));System.out.println(numberList); // 輸出: [10, 20, 30]List<Object> objectList = new ArrayList<>();addNumbersToList(objectList, Arrays.asList(100, 200));System.out.println(objectList); // 輸出: [100, 200]List<? super Integer> list = new ArrayList<Number>();list.add(1); // 允許Object obj = list.get(0); // 只能作為Object類型讀取}
}
關鍵點:
- 參數 dest 可以是 List、List 等(父類型容器)。
- 允許添加 Integer 或其子類型的元素。