泛型的定義、用法與類型通配符的使用方式

来源:https://www.cnblogs.com/fwnboke/archive/2018/03/08/8529670.html
-Advertisement-
Play Games

泛型是什麼? 泛型本質是指類型參數化。意思是允許在定義類、介面、方法時使用類型形參,當使用時指定具體類型,所有使用該泛型參數的地方都被統一化,保證類型一致。如果未指定具體類型,預設是Object類型。集合體系中的所有類都增加了泛型,泛型也主要用在集合。 泛型的定義 泛型類:public class ...


泛型是什麼?

泛型本質是指類型參數化。意思是允許在定義類、介面、方法時使用類型形參,當使用時指定具體類型,所有使用該泛型參數的地方都被統一化,保證類型一致。如果未指定具體類型,預設是Object類型。集合體系中的所有類都增加了泛型,泛型也主要用在集合。  

泛型的定義

泛型類:public class Demo<T> {} ,T表示未知類型。 泛型介面:public interface ImplDemo<T,V>{} ,和定義類一樣(介面就是一個特殊類)。 泛型方法:public <T>  void demo1(T name){System.out.println(name);} , public <T> T demo2(T t){ return t;}  

泛型的好處

  1. 編譯時確定類型,保證類型安全,避免類型轉換異常。
  2. 避免了強制類型轉換。
  3. 代碼利於重用,增加通用性。

泛型的限制和規則

  • 泛型的類型參數只能是引用類型,不能使用值類型。
  • 泛型的類型參數可以有多個。
  • 泛型類不是真正存在的類,不能使用instanceof運算符。
  • 泛型類的類型參數不能用在靜態申明。
  • 如果定義了泛型,不指定具體類型,泛型預設指定為Ojbect類型。
  • 泛型使用?作為類型通配符,表示未知類型,可以匹配任何類型。因為是未知,所以無法添加元素。
  • 類型通配符上限:<? extends T>,?代表是T類型本身或者是T的子類型。常用於泛型方法,避免類型轉換。
  • 類型通配符下限。<? super T>,?代表T類型本身或者是T的父類型。
  • 除了通配符可以實現限制,類、介面和方法中定義的泛型參數也能限制上限和下限。
 

泛型代碼實例

接下來,使用代碼來演示泛型的用途,建議使用目錄查看具體內容。  

集合類演示泛型

		//未指定泛型
		TreeSet ts = new TreeSet();
		ts.add(10);
		ts.add(25);
		ts.add("30");
		System.out.println(ts);//運行時報錯,類型轉換異常
		
		//mode 2
		TreeSet<Integer> ts2 = new TreeSet<>();
		ts2.add(10);
		ts2.add(25);
		ts2.add("30"); //編譯器提示報錯,無法添加非Integer類型
	

未使用泛型時,可以添加任意元素,因為TreeSet會比較每一個元素,所以運行時會引發類型轉換異常。使用泛型後,只能添加同一個類型,所以不會出錯。

 

定義泛型類

public class Person<T> {
	private T name;//類型是未知的
	
	public Person(T name) {
		this.name = name;
	}	
	
	public T getName() {
		return name;
	}
	
	public void sexName(T name) {
		this.name = name;
	}

}
在上面實例中,Person類定義成泛型類,成員變數name的類型指定為T,表示未知類型。實例化該類對象後,可以看到name的類型是Object,表示可以接收任何類型。
		Person p = new Person(10);	//new Person(Object name)
加上泛型後
		//使用泛型兩種方式
		Person<String> ps = new Person<String>(); //new Person<String>(String name)
		Person<String> ps = new Person<>();//new Person<>(T name)
第一種,會直接確定參數類型是什麼,而第二種的參數類型是T ,但如果加入的非String類型,編譯器會檢查並報錯。兩者區別不大。在JDK1.7之後使用第二種,會自動檢查泛型,可以省略後部分<>的泛型參數,建議使用第二種。


定義泛型介面

interface A<T>{
	void display(T value); 
	T getValue(T v);
}

//未對泛型介面指定具體類型
public class Person implements A{

	@Override
	public void display(Object obj) {
		System.out.println();
	}

	@Override
	public Object getValue(Object v) {
		return null;
	}
	
}
如果我們定義了泛型,不指定具體類型,預設就是Object類型。當我們為泛型介面指定具體類型後,代碼如下:

 

//泛型介面
interface A<T>{
	void display(T value); 
	T getValue(T v);
}

//為泛型介面指定具體類型
public class Person implements A<String>{
	@Override
	public void display(String value) {
	}
	@Override
	public String getValue(String v) {
		return null;
	}
}
泛型介面指定具體類型後,所有使用了該泛型參數的地方都被統一化。其實泛型介面和泛型類是一樣的寫法。

 

定義泛型方法

先使用常規方法進行對比。

	public static void main(String[] args) {
		int[] arr = new int[] {1, 8, 15, 6, 3};
		double[] douArr = {10.5, 25.1, 4.9, 1.8};
		String[] strArr = {"我","是","字","符","串"};
		forArr(strArr);
		
	}
	
//遍曆數組的重載方法,支持int和double類型	
	public static void forArr(int[] arr) {
		for(int i=0; i<arr.length; i++) {
			System.out.println(arr[i]);
		}
	}
	//重載了
	public static void forArr(double[] arr) {
		for(double d : arr) {
			System.out.println(d);
		}
	}
	//……
	//……

 

如上所示,如果想遍歷Stirng類型數組,那就還要再次重載代碼,如果是八種類型都有,代碼量非常龐大。使用泛型方法全部通用,代碼如下:

 

	public static void main(String[] args) {
		Integer[] arr =  {1, 8, 15, 6, 3};
		Double[] douArr = {10.5, 25.1, 4.9, 1.8};
		String[] strArr = {"我","是","字","符","串"};
		
		forArrGenric(strArr);
		
	}
	//泛型方法
	public static <T> void forArrGenric(T[] arr) {
		for(int i=0; i < arr.length; i++) {
			System.out.println(arr[i]);
		}
	}
只需定義一個泛型方法,根據運行時傳入的參數類型,動態地獲取類型,就能做到遍歷所有類型數組。但需要註意,泛型的類型參數只能是引用類型,值類型無法在泛型中使用,所以上面的數組都改成了引用類型。值類型需要使用對應的包裝類類型。

 

 

 

使用類型通配符

使用之前,先使用常規方式來進行比較。
	public static void main(String[] args) {
		HashSet hs = new HashSet();
		hs.add("A");
		hs.add("QQ");
		hs.add("Alipay");

		new Test2().test2(hs);
	}	
	//普通遍歷Set集合,Set是泛型介面,沒指定具體泛型參數會引起警告
	public void test(Set s) {
		for(Object o : s)
			System.out.println(o);
	}	
	//增加泛型參數,參數類型是Set<Object>
	public void test2(Set<Object> s) {
		for(Object o : s)
			System.out.println(o);
	}
方法參數的Set集合使用了泛型參數<Object>,方便將參數類型轉換成Object,看起來沒什麼錯。當傳入一個帶泛型參數的集合時,會出現編譯錯誤。代碼如下:

 

 

	public static void main(String[] args) {
		HashSet<String> hs = new HashSet();
		hs.add("A");
		hs.add("QQ");
		hs.add("Alipay");
		new Test2().test2(hs); //error
	}	
	//增加泛型參數,參數類型是Set<Object>
	public void test2(Set<Object> s) {
		for(Object o : s)
			System.out.println(o);
	}
因為泛型類不是真正存在的類,所以Set<String>和Set<Object>不存在關係,自然無法作為參數傳入進去。這時我們就可以使用類型通配符,如下:

 

 

	//使用類型通配符作為類型參數
	public void test2(Set<?> s) {
		for(Object o : s)
			System.out.println(o);
	}

 

Set<?>表示可以匹配任意的泛型Set。雖然可以使用各種泛型Set了。但弊端就是類型未知,所以無法添加元素。還有範圍過於廣泛,所以這時可以考慮限制的類型通配符。
 

限制的類型通配符

上面代碼只要是泛型Set都允許被遍歷,如果只想類型通配符表示一個類和其子類本身呢?設置類型通配符上限,代碼如下:

 

 

public class Test2 {
	public static void main(String[] args) {
		ArrayList<Test2> ar = new ArrayList<>();
		List<Test3> lt = new ArrayList<>();
		List<String> lStr = new ArrayList<>();
		demo(ar);
		demo(lt);
		demo(lStr); //error
	}	
	//限制的類型通配符
	public static void demo(List<? extends Test2> t) {
		for(int i = 0; i < t.size(); i++) {
			System.out.println(t.get(i));
		}
	}
}
class Test3 extends Test2{}//子類
<? extends T>:表示類型是T本身或者是T類型的子類類型。

 

<? super T>:表示類型是T類型本身或者是T類型的父類類型。叫做類型通配符的下限。使用方式都差不多。

 

 

 

泛型為何不能應用於靜態申明的實例解析

先給一個例子,在靜態變數中和靜態代碼塊中使用泛型。
public class Test<T> {
	public static T name;  //error
	public T sex ;
	
	static {
		T ab; //error
	}
}
報出異常:不能使一個靜態引用指向一個非靜態的類型 T。靜態和非靜態之分就在於靜態是編譯時類型,動態是運行時類型。T代表未知類型,如果可以用於靜態申明,因為是未知類型,系統沒法指定初始值,手動賦值也不行,因為不知道啥類型,只有運行時才可以指定。而泛型存在的意義就是為了動態指定具體類型,增強靈活性和通用性,所以用於靜態聲明違背了使用原則。為什麼實例變數和實例方法可以使用呢?因為當你使用實例變數或者方法時,就說明對象存在了,即代表泛型參數也指定了。未指定具體類型預設是Object類型。  

為什麼靜態方法中可以定義泛型方法呢?

先給三個實例,我們來慢慢分析。
public class Test<T> {
	public static void main(String[] args) {
		
	}
	
	//泛型方法
	public T demo1(T t) {
		return t;
	}
	
	//靜態方法使用泛型參數
//	public static T demo2(T t) { return t;}
				
	//定義泛型靜態方法
	public static <W> void demo3(W w) {
		System.out.println(w);
	}  
}
首先,要明確一點,泛型作用是確定具體類型。先看一個泛型方法,使用了泛型參數T作為返回值,當使用對象時來調用該方法時,T類型會變成具體類型。第二個泛型方法是靜態的,使用了T作為返回值和方法參數類型,但是靜態方法是屬於類的,類直接調用的話,T類型無法指定具體類型,那麼該方法就沒有意義。所以直接報錯。第三個也是靜態方法,但是該靜態方法是自定義一個泛型參數,並非使用類型參數。所以當傳入一個具體類型時,該靜態方法的<W>就是具體類型了。兩者靜態方法的區別就是一個是使用泛型參數,一個是定義泛型方法。    
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • html頁面: note:註意不要使用form標簽 JS事件: ...
  • 今天心血來潮,打開了sublime想玩玩react,然後大家都知道的先引入一大串 就是在百度靜態資源庫里找到的。 然後貼html代碼 對的,沒錯,就這麼一行,畢竟只是測試嘛 然後js代碼 大家一定要註意,在script的開頭標簽里,一定要註明,否則瀏覽器會報錯,解析不了。 就按照這樣的代碼,照理來說 ...
  • 1.url: 要求為String類型的參數,(預設為當前頁地址)發送請求的地址。 2.type: 要求為String類型的參數,請求方式(post或get)預設為get。註意其他http請求方法,例如put和delete也可以使用,但僅部分瀏覽器支持。 3.timeout: 要求為Number類型的 ...
  • 面向對象六大原則 單一職責原則——SRP 開閉原則——OCP 里式替換原則——LSP 依賴倒置原則——DIP 介面隔離原則——ISP 迪米特原則——LOD ...
  • 如今,市場環境紛繁複雜,瞬息萬變,現代企業為了更好地生存,需要有極強的適應能力。快速而輕鬆地迎接改變,成為了一個優質企業的特征之一,同時企業還要求技術團隊構建更科學的架構,搭建成本更低的平臺,這就使得這些團隊越來越傾向於使用微服務架構來應對以上要求。微服務的做法有利於軟體組件和數據的分散化,將一個整 ...
  • 工作流模塊 1.模型管理 :web線上流程設計器、預覽流程xml、導出xml、部署流程 2.流程管理 :導入導出流程資源文件、查看流程圖、根據流程實例反射出流程模型、激活掛起 3.運行中流程:查看流程信息、當前任務節點、當前流程圖、作廢暫停流程、指派待辦人 4.歷史的流程:查看流程信息、流程用時、流 ...
  • 本篇的題目其實比較大,所以在寫的時候,我其實是有些“惶恐”的,怕這篇完成後有標題檔的嫌疑。不過為了將自己過去多年的經歷和最近1年改造架構的想法,做一個階段性總結,還是有必要好好寫一寫的,所以如果寫得不好,大家多包涵,歡迎大家補充。 定義目標 既然我們的目標是做到高可用,那麼我們就有必要先明確清楚高可 ...
  • 簡單工廠模式是屬於創建型模式,又叫做靜態工廠方法(Static Factory Method)模式,但不屬於23種GOF設計模式之一。簡單工廠模式是由一個工廠對象決定創建出哪一種產品類的實例。簡單工廠模式是工廠模式家族中最簡單實用的模式,可以理解為是不同工廠模式的一個特殊實現. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...