亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java SE 泛型原理、語法與實踐指南

 更新時間:2025年11月05日 11:15:07   作者:觀望過往  
本文介紹了Java泛型的核心概念與應(yīng)用價值,文章從集合框架的示例切入,對比了泛型使用前后的差異,展示了泛型在編譯時類型檢查、消除強制轉(zhuǎn)換、代碼復(fù)用和提高可讀性方面的優(yōu)勢,感興趣的朋友跟隨小編一起看看吧

泛型(Generics)是 Java SE 5 引入的核心特性,其本質(zhì)是 “參數(shù)化類型”—— 允許在定義類、接口、方法時指定 “類型參數(shù)”,在使用時再明確具體類型(如List<String>Map<Integer, User>)。它像 “類型的占位符”,既能保證編譯時的類型安全(避免ClassCastException),又能減少重復(fù)代碼(實現(xiàn)通用邏輯復(fù)用),是集合框架、工具類、框架開發(fā)的基礎(chǔ)。

一、泛型的核心價值:為什么需要泛型?

在泛型出現(xiàn)之前,Java 通過Object類型實現(xiàn) “通用” 邏輯,但存在類型不安全代碼冗余兩大問題。泛型的核心目標(biāo)就是解決這兩個痛點。

1. 無泛型的痛點:以集合為例

(1)類型不安全(運行時拋異常)

ArrayList在 JDK 1.4 及以前的實現(xiàn)中,內(nèi)部用Object[]存儲元素,添加元素時不限制類型,取出時需強制轉(zhuǎn)換,若類型不匹配,編譯時無提示,運行時拋ClassCastException

// 無泛型:ArrayList存儲Object,可添加任意類型
import java.util.ArrayList;
public class NoGenericDemo {
   public static void main(String\[] args) {
       ArrayList list = new ArrayList();
       list.add("Java");       // 添加String
       list.add(123);          // 添加Integer(編譯不報錯,隱藏風(fēng)險)
       list.add(new Object()); // 添加Object
       // 取出元素:需強制轉(zhuǎn)換為String,運行時出錯
       for (int i = 0; i < list.size(); i++) {
           String str = (String) list.get(i); // 第2個元素是Integer,運行拋ClassCastException
           System.out.println(str.length());
       }
   }
}

問題本質(zhì):編譯階段無法校驗元素類型,錯誤延遲到運行時,風(fēng)險不可控。

(2)代碼冗余(重復(fù)強制轉(zhuǎn)換)

即使存儲的是同一類型,每次取出都需強制轉(zhuǎn)換,代碼繁瑣:

ArrayList list = new ArrayList();
list.add("Apple");
list.add("Banana");
// 每次取出都需強制轉(zhuǎn)換為String,冗余且易出錯
String apple = (String) list.get(0);
String banana = (String) list.get(1);

2. 泛型的解決方案:類型參數(shù)化

通過泛型指定 “元素類型”,編譯階段即可校驗類型合法性,避免強制轉(zhuǎn)換:

// 有泛型:ArrayList\<String>指定元素只能是String
import java.util.ArrayList;
public class GenericDemo {
   public static void main(String\[] args) {
       ArrayList\<String> list = new ArrayList<>(); // 菱形語法,JDK 7+可省略右側(cè)類型
       list.add("Java");       // 合法:String類型
       // list.add(123);        // 編譯報錯:不允許添加Integer,類型安全校驗
       // list.add(new Object()); // 編譯報錯:類型不匹配
       // 取出元素:無需強制轉(zhuǎn)換,直接是String類型
       for (String str : list) {
           System.out.println(str.length()); // 安全調(diào)用String方法
       }
   }
}

3. 泛型的核心價值總結(jié)

核心價值說明示例
編譯時類型安全限制集合 / 對象的元素類型,不允許添加不匹配類型,提前暴露錯誤ArrayList<String>不允許添加 Integer
消除強制轉(zhuǎn)換使用時無需手動轉(zhuǎn)換類型,代碼簡潔且避免ClassCastExceptionString str = list.get(0)(無需強轉(zhuǎn))
代碼復(fù)用一套通用邏輯適配多種類型,無需為每種類型寫重復(fù)代碼(如泛型工具類)GenericUtil.swap<T>(T[] arr, int i, int j)適配所有數(shù)組類型
清晰的代碼意圖類型參數(shù)明確告知數(shù)據(jù)類型,代碼可讀性更高Map<Long, User>明確鍵是 Long,值是 User

二、泛型的基本語法:類、接口、方法

泛型的使用場景分為三類:泛型類(含枚舉)、泛型接口、泛型方法,每種場景的語法略有差異,但核心都是 “定義類型參數(shù),使用時指定具體類型”。

1. 泛型類:類定義時指定類型參數(shù)

泛型類是 “包含類型參數(shù)的類”,在創(chuàng)建對象時需明確具體類型(如List<String>、HashMap<K, V>)。

(1)語法格式

// 定義泛型類:\<T1, T2, ...>中T1、T2是類型參數(shù)(占位符)
class 類名\<T1, T2, ...> {
   // 1. 用類型參數(shù)定義成員變量
   private T1 field1;
   private T2 field2;
   // 2. 用類型參數(shù)定義構(gòu)造方法
   public 類名(T1 field1, T2 field2) {
       this.field1 = field1;
       this.field2 = field2;
   }
   // 3. 用類型參數(shù)定義方法的返回值或參數(shù)
   public T1 getField1() {
       return field1;
   }
   public void setField2(T2 field2) {
       this.field2 = field2;
   }
}

(2)類型參數(shù)命名規(guī)范(約定俗成)

為提高可讀性,類型參數(shù)通常用單個大寫字母表示,常見約定:

  • T:Type(通用類型,最常用);
  • E:Element(集合元素類型,如List<E>);
  • K:Key(鍵類型,如Map<K, V>);
  • V:Value(值類型,如Map<K, V>);
  • N:Number(數(shù)值類型,如Integer、Double);
  • SU、V:多個類型參數(shù)時的后續(xù)占位符(如Class<S, U>)。

(3)代碼示例:自定義泛型類(Pair)

// 泛型類:存儲一對不同類型的值(如鍵值對、名稱-年齡等)
class Pair\<K, V> {
   private K key;
   private V value;
   // 泛型構(gòu)造方法
   public Pair(K key, V value) {
       this.key = key;
       this.value = value;
   }
   // 泛型方法(返回值為類型參數(shù))
   public K getKey() {
       return key;
   }
   public V getValue() {
       return value;
   }
   // 泛型方法(參數(shù)為類型參數(shù))
   public void setValue(V value) {
       this.value = value;
   }
   // 普通方法:打印鍵值對
   @Override
   public String toString() {
       return "Pair{" + "key=" + key + ", value=" + value + "}";
   }
}
// 使用泛型類
public class GenericClassDemo {
   public static void main(String\[] args) {
       // 1. 創(chuàng)建Pair\<String, Integer>對象:key是String,value是Integer
       Pair\<String, Integer> userPair = new Pair<>("張三", 20);
       String userName = userPair.getKey(); // 無需強轉(zhuǎn),直接是String
       Integer userAge = userPair.getValue(); // 無需強轉(zhuǎn),直接是Integer
       System.out.println(userPair); // 輸出:Pair{key=張三, value=20}
       // 2. 創(chuàng)建Pair\<Long, User>對象:key是Long,value是自定義User類
       User user = new User(1001, "李四");
       Pair\<Long, User> userInfoPair = new Pair<>(1001L, user);
       Long userId = userInfoPair.getKey();
       User userInfo = userInfoPair.getValue();
       System.out.println(userInfoPair); // 輸出:Pair{key=1001, value=User{id=1001, name='李四'}}
       // 3. 錯誤示例:添加不匹配類型
       // userPair.setValue("25"); // 編譯報錯:需傳入Integer類型,不能傳String
   }
}
// 自定義User類(用于示例)
class User {
   private Long id;
   private String name;
   public User(Long id, String name) {
       this.id = id;
       this.name = name;
   }
   @Override
   public String toString() {
       return "User{" + "id=" + id + ", name='" + name + "'}";
   }
}

2. 泛型接口:接口定義時指定類型參數(shù)

泛型接口與泛型類類似,在定義接口時指定類型參數(shù),實現(xiàn)接口時需明確具體類型(或繼續(xù)保留泛型)。

(1)語法格式

// 定義泛型接口
interface 接口名\<T1, T2, ...> {
   // 1. 用類型參數(shù)定義抽象方法的返回值或參數(shù)
   T1 method1(T2 param);
   // 2. 用類型參數(shù)定義常量(JDK 1.8+允許接口有默認(rèn)方法和靜態(tài)方法)
   default void method2(T1 param) {
       // 默認(rèn)方法實現(xiàn)
   }
}

(2)代碼示例:自定義泛型接口(Converter)

// 泛型接口:定義“類型轉(zhuǎn)換”行為,T是源類型,R是目標(biāo)類型
interface Converter\<T, R> {
   // 抽象方法:將T類型轉(zhuǎn)換為R類型
   R convert(T source);
   // 默認(rèn)方法:批量轉(zhuǎn)換(JDK 1.8+)
   default List\<R> convertList(List\<T> sourceList) {
       List\<R> resultList = new ArrayList<>();
       for (T source : sourceList) {
           resultList.add(convert(source));
       }
       return resultList;
   }
}
// 實現(xiàn)泛型接口:String→Integer轉(zhuǎn)換器
class StringToIntegerConverter implements Converter\<String, Integer> {
   @Override
   public Integer convert(String source) {
       // 實現(xiàn)String到Integer的轉(zhuǎn)換(處理空值)
       return source == null ? 0 : Integer.parseInt(source.trim());
   }
}
// 測試泛型接口
public class GenericInterfaceDemo {
   public static void main(String\[] args) {
       // 1. 創(chuàng)建轉(zhuǎn)換器實例
       Converter\<String, Integer> converter = new StringToIntegerConverter();
       // 2. 單個轉(zhuǎn)換
       Integer num = converter.convert("123");
       System.out.println("單個轉(zhuǎn)換結(jié)果:" + num); // 輸出:123
       // 3. 批量轉(zhuǎn)換(調(diào)用默認(rèn)方法)
       List\<String> strList = Arrays.asList("456", "789", "1000");
       List\<Integer> intList = converter.convertList(strList);
       System.out.println("批量轉(zhuǎn)換結(jié)果:" + intList); // 輸出:\[456, 789, 1000]
       // 4. 其他實現(xiàn):如Integer→String轉(zhuǎn)換器(匿名內(nèi)部類)
       Converter\<Integer, String> intToStringConverter = new Converter\<Integer, String>() {
           @Override
           public String convert(Integer source) {
               return source == null ? "0" : "數(shù)值:" + source;
           }
       };
       String str = intToStringConverter.convert(5);
       System.out.println("Integer→String轉(zhuǎn)換:" + str); // 輸出:數(shù)值:5
   }
}

3. 泛型方法:方法定義時指定類型參數(shù)

泛型方法是 “包含類型參數(shù)的方法”,獨立于泛型類 / 接口—— 即使在普通類中,也可定義泛型方法,調(diào)用時需明確類型(或由編譯器自動推斷)。

(1)語法格式

// 泛型方法:\<T1, T2, ...>放在返回值前,是方法的類型參數(shù)
修飾符 \<T1, T2, ...> 返回值類型 方法名(T1 param1, T2 param2, ...) {
   // 方法體:使用類型參數(shù)
}

(2)關(guān)鍵區(qū)別:泛型方法 vs 泛型類的方法

  • 泛型類的方法:類型參數(shù)屬于類,創(chuàng)建類對象時確定類型(如Pair<K, V>getKey()方法,K 的類型在new Pair<>()時確定);
  • 泛型方法:類型參數(shù)屬于方法,調(diào)用方法時確定類型(或編譯器推斷),與類是否泛型無關(guān)(普通類也可定義泛型方法)。

(3)代碼示例:自定義泛型工具方法

import java.util.Arrays;
// 普通類(非泛型類)中定義泛型方法
class GenericUtil {
   // 泛型方法1:交換數(shù)組中兩個位置的元素(適配所有類型的數(shù)組)
   public static \<T> void swap(T\[] arr, int i, int j) {
       // 校驗參數(shù)合法性
       if (arr == null || i < 0 || j < 0 || i >= arr.length || j >= arr.length) {
           throw new IllegalArgumentException("參數(shù)非法!");
       }
       T temp = arr\[i];
       arr\[i] = arr\[j];
       arr\[j] = temp;
   }
   // 泛型方法2:獲取數(shù)組中的第一個元素(適配所有類型的數(shù)組)
   public static \<T> T getFirstElement(T\[] arr) {
       if (arr == null || arr.length == 0) {
           return null;
       }
       return arr\[0];
   }
   // 泛型方法3:帶邊界的泛型方法(后續(xù)“泛型邊界”章節(jié)詳解)
   public static \<T extends Comparable\<T>> T getMax(T\[] arr) {
       if (arr == null || arr.length == 0) {
           return null;
       }
       T max = arr\[0];
       for (T element : arr) {
           if (element.compareTo(max) > 0) { // 調(diào)用Comparable接口方法
               max = element;
           }
       }
       return max;
   }
}
// 測試泛型方法
public class GenericMethodDemo {
   public static void main(String\[] args) {
       // 1. 交換String數(shù)組元素(編譯器自動推斷T為String)
       String\[] strArr = {"A", "B", "C", "D"};
       GenericUtil.swap(strArr, 1, 3); // 交換索引1和3的元素
       System.out.println("交換后String數(shù)組:" + Arrays.toString(strArr)); // 輸出:\[A, D, C, B]
       // 2. 交換Integer數(shù)組元素(顯式指定T為Integer,也可省略)
       Integer\[] intArr = {1, 2, 3, 4};
       GenericUtil.\<Integer>swap(intArr, 0, 2); // 顯式指定類型
       System.out.println("交換后Integer數(shù)組:" + Arrays.toString(intArr)); // 輸出:\[3, 2, 1, 4]
       // 3. 獲取數(shù)組第一個元素
       Integer firstInt = GenericUtil.getFirstElement(intArr);
       System.out.println("Integer數(shù)組第一個元素:" + firstInt); // 輸出:3
       // 4. 獲取數(shù)組最大值(T需實現(xiàn)Comparable接口)
       Integer\[] numArr = {5, 2, 9, 1};
       Integer maxNum = GenericUtil.getMax(numArr);
       System.out.println("數(shù)組最大值:" + maxNum); // 輸出:9
       // 錯誤示例:非Comparable類型無法調(diào)用getMax(編譯報錯)
       // User\[] userArr = {new User(1L, "張三"), new User(2L, "李四")};
       // GenericUtil.getMax(userArr); // 編譯報錯:User未實現(xiàn)Comparable\<User>
   }
}

三、泛型的核心特性:類型擦除(Type Erasure)

Java 的泛型是 “編譯時泛型”—— 編譯階段會將泛型的 “類型參數(shù)” 擦除為 “原始類型”(Raw Type),運行時 JVM 不感知泛型類型,僅保留原始類型信息。這是 Java 泛型與 C++ 模板的核心區(qū)別(C++ 模板在編譯時生成不同類型的代碼)。

1. 類型擦除的規(guī)則

(1)無邊界泛型:擦除為Object

若泛型類型參數(shù)無顯式上界(如T),編譯后擦除為Object

// 泛型類:無邊界
class Box\<T> {
   private T value;
   public T getValue() { return value; }
   public void setValue(T value) { this.value = value; }
}
// 編譯后擦除為原始類型(偽代碼)
class Box {
   private Object value;
   public Object getValue() { return value; }
   public void setValue(Object value) { this.value = value; }
}

(2)有上界泛型:擦除為 “上界類型”

若泛型類型參數(shù)有顯式上界(如T extends Number),編譯后擦除為上界類型(Number):

// 泛型類:有上界(T必須是Number的子類)
class NumberBox\<T extends Number> {
   private T value;
   public T getValue() { return value; }
   public void setValue(T value) { this.value = value; }
}
// 編譯后擦除為原始類型(偽代碼)
class NumberBox {
   private Number value;
   public Number getValue() { return value; }
   public void setValue(Number value) { this.value = value; }
}

(3)泛型方法的擦除

泛型方法的類型參數(shù)同樣會擦除,無邊界擦除為Object,有上界擦除為上界類型:

// 泛型方法:無邊界
public static \<T> T getFirst(T\[] arr) { ... }
// 編譯后擦除為(偽代碼)
public static Object getFirst(Object\[] arr) { ... }
// 泛型方法:有上界
public static \<T extends Comparable\<T>> T getMax(T\[] arr) { ... }
// 編譯后擦除為(偽代碼)
public static Comparable getMax(Comparable\[] arr) { ... }

2. 類型擦除的影響:橋方法(Bridge Method)

類型擦除可能導(dǎo)致 “重寫方法簽名不匹配”,JVM 會自動生成 “橋方法” 解決此問題。

代碼示例:橋方法的生成

// 泛型接口:Comparable\<T>(JDK自帶)
interface Comparable\<T> {
   int compareTo(T o);
}
// 實現(xiàn)類:String實現(xiàn)Comparable\<String>
class String implements Comparable\<String> {
   // 實現(xiàn)compareTo方法:參數(shù)是String
   @Override
   public int compareTo(String anotherString) {
       // 字符串比較邏輯
       return this.equals(anotherString) ? 0 : 1; // 簡化邏輯
   }
}
// 類型擦除后:
// Comparable接口的compareTo方法擦除為int compareTo(Object o)
// String的compareTo方法簽名是int compareTo(String o),與擦除后的接口方法不匹配
// JVM自動生成橋方法(偽代碼):
class String implements Comparable {
   // 1. 橋方法:匹配擦除后的接口方法
   public int compareTo(Object o) {
       // 調(diào)用實際的compareTo(String)方法
       return compareTo((String) o);
   }
   // 2. 實際實現(xiàn)的方法
   public int compareTo(String anotherString) {
       return this.equals(anotherString) ? 0 : 1;
   }
}

橋方法的作用:確保類型擦除后,子類仍能正確重寫父類 / 接口的方法,避免多態(tài)失效。

3. 類型擦除的局限性

  • 運行時無法獲取泛型類型信息(如new ArrayList<String>()運行時僅知道是ArrayList,不知道元素是String);
  • 無法實例化泛型類型的對象(如new T()編譯報錯,因擦除后TObject,無法確定具體類型);
  • 無法創(chuàng)建泛型類型的數(shù)組(如new T[10]編譯報錯,因數(shù)組在運行時需知道具體類型)。

四、泛型的通配符:解決泛型類型的靈活性問題

泛型的 “類型參數(shù)” 是 “嚴(yán)格匹配” 的(如List<String>不是List<Object>的子類),但實際開發(fā)中常需 “靈活匹配多個類型”(如方法接收所有List子類)。泛型通配符(?)就是為解決此問題而生,分為無界通配符、上界通配符、下界通配符三類。

1. 無界通配符(?):匹配任意類型

無界通配符?表示 “任意類型”,常用于 “不關(guān)心泛型類型,僅使用原始類型方法” 的場景(如獲取集合大小、判斷是否為空)。

(1)語法格式

// 無界通配符:?表示任意類型
List\<?> list; // 可指向List\<String>、List\<Integer>、List\<User>等任意List

(2)代碼示例:無界通配符的使用

import java.util.ArrayList;
import java.util.List;
public class UnboundedWildcardDemo {
   // 方法:打印任意List的大小和元素(不關(guān)心元素類型)
   public static void printList(List\<?> list) {
       System.out.println("List大?。? + list.size());
       for (Object obj : list) { // 只能用Object接收元素(因類型未知)
           System.out.print(obj + " ");
       }
       System.out.println();
   }
   public static void main(String\[] args) {
       // 1. List\<String>
       List\<String> strList = new ArrayList<>();
       strList.add("A");
       strList.add("B");
       printList(strList); // 合法:List\<String>匹配List\<?>
       // 2. List\<Integer>
       List\<Integer> intList = new ArrayList<>();
       intList.add(1);
       intList.add(2);
       printList(intList); // 合法:List\<Integer>匹配List\<?>
       // 3. 無界通配符的限制:無法添加非null元素(因類型未知,無法確定是否匹配)
       List\<?> wildcardList = new ArrayList\<String>();
       // wildcardList.add("C"); // 編譯報錯:無法確定類型,不允許添加
       wildcardList.add(null); // 合法:null是任意類型的實例
   }
}

(3)核心特點:

  • 優(yōu)點:靈活匹配任意泛型類型,適合 “只讀” 或 “不依賴元素類型” 的場景;
  • 缺點:無法添加非null元素(類型未知,避免添加不匹配類型),僅能通過Object接收元素。

2. 上界通配符(? extends T):匹配 T 及其子類

上界通配符? extends T表示 “任意繼承自 T 的類型”(T 是上界),常用于 “讀取元素” 的場景(如獲取集合中元素的最大值,元素需是 T 的子類)。

(1)語法格式

// 上界通配符:? extends T,匹配T及其子類
List\<? extends Number> list; // 可指向List\<Integer>、List\<Double>、List\<Number>(Integer和Double是Number的子類)

(2)代碼示例:上界通配符的使用

import java.util.ArrayList;
import java.util.List;
public class BoundedUpperWildcardDemo {
   // 方法:計算List中所有數(shù)值的和(元素必須是Number的子類,如Integer、Double)
   public static double sumList(List\<? extends Number> numberList) {
       double sum = 0.0;
       for (Number num : numberList) { // 可通過上界類型Number接收元素
           sum += num.doubleValue(); // 調(diào)用Number的方法,安全
       }
       return sum;
   }
   public static void main(String\[] args) {
       // 1. List\<Integer>(Integer extends Number)
       List\<Integer> intList = new ArrayList<>();
       intList.add(10);
       intList.add(20);
       System.out.println("Integer列表和:" + sumList(intList)); // 輸出:30.0
       // 2. List\<Double>(Double extends Number)
       List\<Double> doubleList = new ArrayList<>();
       doubleList.add(15.5);
       doubleList.add(25.5);
       System.out.println("Double列表和:" + sumList(doubleList)); // 輸出:41.0
       // 3. 上界通配符的限制:無法添加非null元素(除null外)
       List\<? extends Number> upperList = new ArrayList\<Integer>();
       // upperList.add(30); // 編譯報錯:無法確定具體是Number的哪個子類,避免添加不匹配類型
       upperList.add(null); // 合法:null是任意類型的實例
   }
}

(3)核心特點:

  • 優(yōu)點:可通過上界類型安全讀取元素(如Number),適合 “只讀” 場景;
  • 缺點:無法添加非null元素(類型不確定,如List<? extends Number>可能是List<Integer>,添加Double會出錯)。

3. 下界通配符(? super T):匹配 T 及其父類

下界通配符? super T表示 “任意 T 的父類型”(T 是下界),常用于 “寫入元素” 的場景(如向集合中添加 T 類型的元素,父類集合可接收子類元素)。

(1)語法格式

// 下界通配符:? super T,匹配T及其父類
List\<? super Integer> list; // 可指向List\<Integer>、List\<Number>、List\<Object>(Number和Object是Integer的父類)

(2)代碼示例:下界通配符的使用

import java.util.ArrayList;
import java.util.List;
public class BoundedLowerWildcardDemo {
   // 方法:向List中添加多個Integer元素(List的類型必須是Integer或其父類)
   public static void addIntegers(List\<? super Integer> list) {
       list.add(1); // 合法:可添加Integer類型(下界類型)
       list.add(2); // 合法:可添加Integer的子類(如Integer本身)
       // list.add(3.5); // 編譯報錯:不能添加非Integer類型(如Double)
   }
   public static void main(String\[] args) {
       // 1. List\<Integer>(Integer super Integer)
       List\<Integer> intList = new ArrayList<>();
       addIntegers(intList);
       System.out.println("List\<Integer>添加后:" + intList); // 輸出:\[1, 2]
       // 2. List\<Number>(Number super Integer)
       List\<Number> numberList = new ArrayList<>();
       numberList.add(100.5); // 先添加一個Double
       addIntegers(numberList);
       System.out.println("List\<Number>添加后:" + numberList); // 輸出:\[100.5, 1, 2]
       // 3. List\<Object>(Object super Integer)
       List\<Object> objectList = new ArrayList<>();
       objectList.add("Hello"); // 先添加一個String
       addIntegers(objectList);
       System.out.println("List\<Object>添加后:" + objectList); // 輸出:\[Hello, 1, 2]
       // 4. 下界通配符的限制:讀取元素只能用Object接收(因父類類型不確定)
       for (Object obj : numberList) {
           System.out.print(obj + " "); // 只能用Object接收
       }
   }
}

(3)核心特點:

  • 優(yōu)點:可安全添加下界類型(如Integer)及其子類的元素,適合 “寫入” 場景;
  • 缺點:讀取元素只能用Object接收(父類類型不確定,無法通過更具體的類型接收)。

4. 通配符使用口訣:PECS 原則

為簡化通配符的選擇,業(yè)界總結(jié)出PECS 原則(Producer Extends, Consumer Super):

  • Producer(生產(chǎn)者):若泛型對象僅用于 “提供元素”(讀?。?code>? extends T(上界通配符);
  • 例:sumList(List<? extends Number> list)——list 是 “生產(chǎn)者”,提供 Number 類型元素用于求和。
  • Consumer(消費者):若泛型對象僅用于 “接收元素”(寫入),用? super T(下界通配符);
  • 例:addIntegers(List<? super Integer> list)——list 是 “消費者”,接收 Integer 類型元素。
  • 既是生產(chǎn)者又是消費者:不用通配符,直接用具體類型(如List<T>)。

五、泛型的限制與注意事項

Java 泛型受限于類型擦除,存在一些無法突破的限制,開發(fā)中需避免這些場景。

1. 限制 1:不能實例化泛型類型的對象

因類型擦除后泛型類型變?yōu)?code>Object或上界類型,無法確定具體類型,故new T()編譯報錯:

class Box\<T> {
   public Box() {
       // T obj = new T(); // 編譯報錯:無法實例化泛型類型
       // 解決方案:通過反射或傳入Class對象
       // T obj = clazz.newInstance(); // 需傳入Class\<T> clazz參數(shù)
   }
}

2. 限制 2:不能用基本類型作為類型參數(shù)

泛型的類型參數(shù)必須是 “引用類型”(如Integer、String),不能是基本類型(如int、double),因類型擦除后會變?yōu)?code>Object,而基本類型無法賦值給Object

// List\<int> list = new ArrayList<>(); // 編譯報錯:不能用基本類型int
List\<Integer> list = new ArrayList<>(); // 合法:用包裝類Integer

3. 限制 3:不能創(chuàng)建泛型類型的數(shù)組

數(shù)組在運行時需知道具體類型,而泛型類型擦除后無法確定,故new T[10]編譯報錯:

class Box\<T> {
   public void test() {
       // T\[] arr = new T\[10]; // 編譯報錯:不能創(chuàng)建泛型數(shù)組
       // 解決方案1:用Object數(shù)組,使用時強制轉(zhuǎn)換
       Object\[] arr = new Object\[10];
       T element = (T) arr\[0];
       // 解決方案2:用ArrayList替代數(shù)組(推薦)
       List\<T> list = new ArrayList<>();
   }
}

4. 限制 4:泛型類不能繼承 Throwable

泛型類無法繼承ExceptionError等 Throwable 子類,因異常處理(try-catch)在編譯時需確定類型,而泛型類型擦除后無法匹配:

// class GenericException\<T> extends Exception { } // 編譯報錯:泛型類不能繼承Throwable
class MyException extends Exception { } // 合法:非泛型類可繼承Exception

5. 限制 5:靜態(tài)方法不能引用類的泛型參數(shù)

類的泛型參數(shù)屬于 “對象級”(創(chuàng)建對象時確定),而靜態(tài)方法屬于 “類級”(類加載時確定),二者生命周期不匹配,故靜態(tài)方法不能直接引用類的泛型參數(shù):

class Box\<T> {
   // public static T getValue() { return null; } // 編譯報錯:靜態(tài)方法不能引用類的泛型參數(shù)
   // 解決方案:靜態(tài)方法定義自己的泛型參數(shù)(泛型方法)
   public static \<U> U getValue() { return null; } // 合法:靜態(tài)泛型方法
}

六、泛型的實際應(yīng)用場景

泛型在 Java 開發(fā)中無處不在,核心應(yīng)用場景包括集合框架、工具類、框架設(shè)計等。

1. 場景 1:集合框架(最典型應(yīng)用)

Java 集合框架(List、SetMap等)全部基于泛型實現(xiàn),確保元素類型安全:

// List\<String>:元素只能是String
List\<String> names = new ArrayList<>();
names.add("Alice");
String name = names.get(0); // 無需強轉(zhuǎn)
// Map\<Long, User>:鍵是Long,值是User
Map\<Long, User> userMap = new HashMap<>();
userMap.put(1001L, new User(1001L, "Bob"));
User user = userMap.get(1001L); // 無需強轉(zhuǎn)

2. 場景 2:泛型工具類(代碼復(fù)用)

開發(fā)通用工具類(如排序、比較、轉(zhuǎn)換工具)時,用泛型實現(xiàn) “一套邏輯適配多種類型”:

// 泛型工具類:排序任意Comparable類型的數(shù)組
public class SortUtil {
   public static \<T extends Comparable\<T>> void sort(T\[] arr) {
       for (int i = 0; i < arr.length - 1; i++) {
           for (int j = 0; j < arr.length - 1 - i; j++) {
               if (arr\[j].compareTo(arr\[j + 1]) > 0) {
                   T temp = arr\[j];
                   arr\[j] = arr\[j + 1];
                   arr\[j + 1] = temp;
               }
           }
       }
   }
}
// 使用:排序Integer數(shù)組和String數(shù)組
Integer\[] intArr = {3, 1, 2};
SortUtil.sort(intArr); // 輸出:\[1, 2, 3]
String\[] strArr = {"C", "A", "B"};
SortUtil.sort(strArr); // 輸出:\[A, B, C]

3. 場景 3:框架設(shè)計(解耦與擴展)

主流框架(如 Spring、MyBatis)大量使用泛型實現(xiàn)靈活擴展,例如 MyBatis 的Mapper接口:

// 泛型接口:MyBatis Mapper,T是實體類,ID是主鍵類型
public interface BaseMapper\<T, ID> {
   T selectById(ID id); // 根據(jù)主鍵查詢
   int insert(T entity); // 插入實體
   int update(T entity); // 更新實體
   int deleteById(ID id); // 根據(jù)主鍵刪除
}
// 實現(xiàn)接口:UserMapper,T=User,ID=Long
public interface UserMapper extends BaseMapper\<User, Long> {
   // 無需重復(fù)定義CRUD方法,直接繼承泛型接口
   List\<User> selectByUsername(String username); // 新增自定義方法
}

七、泛型的常見誤區(qū)與避坑指南

1. 誤區(qū) 1:混淆泛型類型與原始類型

  • 問題:List list = new ArrayList<String>();(原始類型 List 接收泛型對象),編譯時會有 “未檢查的轉(zhuǎn)換” 警告,且失去類型安全;
  • 解決:始終用泛型類型接收泛型對象(如List<String> list = new ArrayList<>();),避免使用原始類型。

2. 誤區(qū) 2:錯誤使用通配符導(dǎo)致添加元素失敗

  • 問題:List<? extends Number> list = new ArrayList<Integer>(); list.add(1);(編譯報錯),誤以為上界通配符可添加元素;
  • 原因:上界通配符? extends Number可能指向List<Double>,添加Integer會類型不匹配;
  • 解決:添加元素用下界通配符(? super T),如List<? super Integer> list = new ArrayList<>(); list.add(1);

3. 誤區(qū) 3:泛型方法的類型參數(shù)與類的類型參數(shù)重名

  • 問題:
class Box\<T> {
   // 泛型方法的類型參數(shù)T與類的T重名,導(dǎo)致混淆
   public \<T> T getValue(T param) { return param; }
}
  • 影響:方法的T會隱藏類的T,導(dǎo)致類的T無法在方法中使用;
  • 解決:泛型方法的類型參數(shù)用不同名稱(如U),避免重名,如public <U> U getValue(U param) { return param; }。

4. 誤區(qū) 4:誤以為泛型可實現(xiàn) “運行時類型判斷”

  • 問題:if (list instanceof List<String>) { ... }(編譯報錯),試圖在運行時判斷泛型類型;
  • 原因:類型擦除后,運行時List<String>List<Integer>都是List,無法區(qū)分;
  • 解決:若需判斷元素類型,遍歷集合檢查每個元素的類型(如if (list.get(0) instanceof String))。

八、總結(jié):泛型的核心要點與實踐建議

1. 核心要點

  • 本質(zhì):參數(shù)化類型,編譯時類型安全,運行時類型擦除;
  • 語法:泛型類(class A<T>)、泛型接口(interface B<T>)、泛型方法(<T> T method(T param));
  • 通配符:PECS 原則(生產(chǎn)者extends,消費者super),無界通配符用于只讀且不關(guān)心類型;
  • 限制:不能實例化泛型對象、不能用基本類型、不能創(chuàng)建泛型數(shù)組、泛型類不能繼承 Throwable。

2. 實踐建議

  • 優(yōu)先使用泛型:定義集合、工具類時強制使用泛型,避免原始類型,確保類型安全;
  • 合理選擇通配符:讀取用extends,寫入用super,既讀又寫用具體類型;
  • 泛型方法優(yōu)先于泛型類:若僅方法需要通用邏輯,定義泛型方法(無需創(chuàng)建泛型類),提高代碼復(fù)用;
  • 避免過度泛型化:僅在需要適配多種類型時使用泛型,簡單場景直接用具體類型,避免代碼復(fù)雜度。

泛型是 Java 類型系統(tǒng)的重要擴展,掌握泛型的語法與原理,是編寫類型安全、高復(fù)用代碼的基礎(chǔ),也是后續(xù)學(xué)習(xí)集合框架、框架源碼(如 Spring、MyBatis)的關(guān)鍵前提。

到此這篇關(guān)于Java SE 泛型原理、語法與實踐詳解?的文章就介紹到這了,更多相關(guān)java se泛型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 深度解析Java中的國際化底層類ResourceBundle

    深度解析Java中的國際化底層類ResourceBundle

    做項目應(yīng)該都會實現(xiàn)國際化,那么大家知道Java底層是如何實現(xiàn)國際化的嗎?這篇文章就來和大家深度解析一下Java中的國際化底層類ResourceBundle,希望對大家有所幫助
    2023-03-03
  • Spring?Cloud詳細(xì)講解zuul集成Eureka流程

    Spring?Cloud詳細(xì)講解zuul集成Eureka流程

    這篇文章主要介紹了Spring?Cloud?zuul集成Eureka,Eureka?Client中內(nèi)置一個負(fù)載均衡器,用來進(jìn)行基本的負(fù)載均衡,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • java發(fā)送url請求獲取返回值的二種方法

    java發(fā)送url請求獲取返回值的二種方法

    這篇文章主要介紹了java發(fā)送url請求獲取返回值的二種方法,需要的朋友可以參考下
    2014-03-03
  • Java中EnumMap代替序數(shù)索引代碼詳解

    Java中EnumMap代替序數(shù)索引代碼詳解

    這篇文章主要介紹了Java中EnumMap代替序數(shù)索引代碼詳解,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
    2018-02-02
  • 舉例講解Java中數(shù)組和字符串類型的使用方法

    舉例講解Java中數(shù)組和字符串類型的使用方法

    這篇文章主要介紹了舉例講解Java中數(shù)組和字符串類型的使用方法,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-09-09
  • 基于Java實現(xiàn)一個簡單的單詞本Android App的實踐

    基于Java實現(xiàn)一個簡單的單詞本Android App的實踐

    本文基于Java實現(xiàn)了一個簡單的單詞本安卓app,用的是SQLite數(shù)據(jù)庫,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • Java中Druid連接池連接超時獲取不到連接的解決

    Java中Druid連接池連接超時獲取不到連接的解決

    這篇文章主要介紹了Java中Druid連接池連接超時獲取不到連接的解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • java 中 String format 和Math類實例詳解

    java 中 String format 和Math類實例詳解

    這篇文章主要介紹了java 中 String format 和Math類實例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • java基本教程之線程讓步 java多線程教程

    java基本教程之線程讓步 java多線程教程

    本文對Thread中的線程讓步方法yield()進(jìn)行介紹,yield()的作用是讓步。它能讓當(dāng)前線程由“運行狀態(tài)”進(jìn)入到“就緒狀態(tài)”,從而讓其它具有相同優(yōu)先級的等待線程獲取執(zhí)行權(quán),大家參考使用吧
    2014-01-01
  • Java常用鎖synchronized和ReentrantLock的區(qū)別

    Java常用鎖synchronized和ReentrantLock的區(qū)別

    這篇文章主要介紹了Java常用鎖synchronized和ReentrantLock的區(qū)別,二者的功效都是相同的,但又有很多不同點,下面我們就進(jìn)入文章了解具體的相關(guān)內(nèi)容吧。需要的小伙伴也可以參考一下
    2022-05-05

最新評論