Java開發筆記(六十八)從泛型方法探究泛型的起源

来源:https://www.cnblogs.com/pinlantu/archive/2019/03/06/10486461.html
-Advertisement-
Play Games

前面介紹各種容器之時,通過在容器名稱後面添加包裹數據類型的一對尖括弧,表示該容器存放的是哪種類型的元素。這樣一來總算把Java當中的各類括弧都湊齊了,例如包裹一段代碼的花括弧、指定數組元素下標的方括弧、容納方法輸入參數的圓括弧,還有最近跟在容器名稱之後的尖括弧。可是為什麼尖括弧要加到容器後面呢?它還 ...


前面介紹各種容器之時,通過在容器名稱後面添加包裹數據類型的一對尖括弧,表示該容器存放的是哪種類型的元素。這樣一來總算把Java當中的各類括弧都湊齊了,例如包裹一段代碼的花括弧、指定數組元素下標的方括弧、容納方法輸入參數的圓括弧,還有最近跟在容器名稱之後的尖括弧。可是為什麼尖括弧要加到容器後面呢?它還能不能用於其它場合?若想對尖括弧的來龍去脈究根問底,就得從泛型的概念說起了。
不管是方法還是類,都支持輸入指定類型的參數,其中方法的輸入參數在調用方法時填寫,而類的輸入參數可通過構造方法傳遞。在這兩種參數輸入的情況中,參數類型是早就確定好的,只有參數值才會動態變化,那要是連參數類型都不確定,得等到方法調用或者創建實例的時候才能確定參數類型,這可如何是好?為解決此種需求,各類編程語言紛紛祭出泛型的絕招,所謂“泛型”它的錶面意思是空泛的類型,也就是不明確的類型,既然類型在方法定義或者類定義時仍不明確,只好留待要用的時候再指定了。
為了更好地理解泛型的根源,接下來先看個簡單的例子。如今有兩個數字,一個是整數1,另一個是帶小數點的1.0,光光從算術方面比較的話,1與1.0肯定相等。但是到了Java語言這裡,使用包裝整型變數保存整數1,使用包裝浮點型變數保存小數1.0f,然後二者通過equals方法進行校驗,判斷結果卻是不等的。此處對整數與小數開展比較的代碼如下所示:

		Integer oneInt = 1;
		Float oneFloat = 1.0f;
		boolean equalsSimple = oneInt.equals(oneFloat);
		System.out.println("equalsSimple="+equalsSimple);

 

運行以上的測試代碼,發現日誌輸出信息為“equalsSimple=false”,該結果看似咄咄怪事,其實是必然的,因為它們的變數類型都不一樣,導致編譯器認為二者的類型尚不吻合,遑論其它。若想進行包裝數值變數之間的相等判斷,就必須把有關變數轉換為相同類型,再作指定精度的數值一致性檢驗。考慮到Integer和Float都繼承自Number類型,系出同源的還有Long、Double等類型,於是可將這些包裝變數統統轉為Number類型,然後從Number變數獲取雙精度數值加以比較。據此編寫的方法代碼示例如下:

	// 通過Number基類比較兩個數值變數是否相等
	public static boolean equalsNumber(Number n1, Number n2) {
		return n1.doubleValue() == n2.doubleValue();
	}

 

從上面的equalsNumber方法可見,它的輸入參數為Number型,同時涵蓋了Number及其派生出來的所有子類。對於這種情況,Java允許泛化參數類型,即先聲明一個由Number擴展而來的類型T,再把T作為輸入參數的變數類型。下麵是具體的類型泛化代碼:

	// 通過泛型變數比較兩個數值變數是否相等。利用尖括弧包裹泛型的派生操作
	public static <T extends Number> boolean equalsGeneric(T t1, T t2) {
		return t1.doubleValue() == t2.doubleValue();
	}

 

雖然equalsNumber與equalsGeneric的參數格式有所不同,但實際上兩個方法是等價的,它們支持的入參類型都屬於Number及其子類。

緊接著再來看個數組元素拼接成字元串的例子,編碼調試的過程中,程式員常常想知道某個數組裡面究竟放了哪些元素,這時便需要將數組的所有元素都列印出來。然而數組變數自身不能自動轉成字元串,只能通過Arrays工具的toString方法輸出拼接好的字元串,倘若由程式員自己編碼去拼接數組元素,那又該如何處理?因為普通的數據類型也同時支持數組形式,所以要想整個通用的字元串拼接方法,必須找到這些數據類型的共同基類,恰好Java也提供了這個基類名叫Object,那末把“Object[]”當作通用的數組類型真是再合適不過了。如此一來,數組各元素的字元串拼接代碼就變成了下麵這般:

	// 把對象數組裡的各個元素拼接成字元串
	public static String objectsToString(Object[] array) {
		String result = "";
		if (array!=null && array.length>0) {
			for (int i=0; i<array.length; i++) {
				if (i > 0) {
					result = result + " | ";
				}
				result = result + array[i].toString();
			}
		}
		return result;
	}

 

接著讓外部運行一段測試代碼,檢查看看字元串拼接是否正常運行,測試代碼如下:

		Double[] doubleArray = new Double[] { 1.1, 2D, 3.1415926, 11.11 };
		System.out.println("objectsToString=" + objectsToString(doubleArray));

 

運行上述的測試代碼,觀察輸出的日誌發現拼接功能完全正常:

objectsToString=1.1 | 2.0 | 3.1415926 | 11.11

 

Object作為普通數據類型的基類,自然它也支持泛化的寫法,即先聲明一個由Object擴展而來的類型T,再把T作為輸入參數的變數類型,於是類型泛化的代碼格式形如“<T extends Object>”。由於Object是Java預設的原始基類,如同大家自定義新類時都沒寫“extends Object”那樣,類型泛化也不必顯式寫明“extends Object”,因此“<T extends Object>”完成可以簡寫為“<T>”。這樣採取泛化簡寫的字元串拼接泛型代碼如下所示:

	// 把泛型數組裡的各個元素拼接成字元串。<T> 等同於 <T extends Object>
	//public static <T extends Object> String arraysToString(T[] array) {
	public static <T> String arraysToString(T[] array) {
		String result = "";
		if (array!=null && array.length>0) {
			for (int i=0; i<array.length; i++) {
				if (i > 0) {
					result = result + " | ";
				}
				result = result + array[i].toString();
			}
		}
		return result;
	}

 

現在給出了數組類型的泛型寫法,容器類型也能依樣畫葫蘆,對應於泛型數組的“T[]”,原先通用的清單數據就變成了類型“List<T>”。改寫之後的清單元素拼接代碼示例如下:

	// 把List清單里的各個元素拼接成字元串,此處使用了泛型
	public static <T> String listToString(List<T> list) {
		String result = "";
		if (list!=null && list.size()>0) {
			for (int i=0; i<list.size(); i++) {
				if (i > 0) {
					result = result + " | ";
				}
				result = result + list.get(i).toString();
			}
		}
		return result;
	}

 

對於包括清單在內的容器類型來說,還能在尖括弧內部填上問號,同樣表示裡面的數據類型是不確定的,就像下列代碼演示的那樣:

	// 把List清單里的各個元素拼接成字元串,此處使用了問號表示不確定類型
	public static String listToStringByQuestion(List<?> list) {
		String result = "";
		if (list!=null && list.size()>0) {
			for (int i=0; i<list.size(); i++) {
				if (i > 0) {
					result = result + " | ";
				}
				result = result + list.get(i).toString();
			}
		}
		return result;
	}

 

不過帶有問號的“<?>”寫法有很大的局限性,它既不如泛型靈活,也不如Object通用。問號寫法僅僅適用於個別場合,並不推薦在一般方法中運用。單單拿問號跟泛型比較的話,主要有以下幾點區別:
1、問號只能用於給泛型類創建實例,本身不能創建實例。而泛型T既可用於泛型類創建實例,也可用於給自身創建實例,如“T t;”
2、問號只可用作輸入參數,不可用作輸出參數。而泛型T用於二者皆可。
3、使用了問號的容器實例,只允許調用get方法,不允許調用add方法。而泛型容器不存在方法調用的限制。



更多Java技術文章參見《Java開發筆記(序)章節目錄


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 首先創建一個test類: 在main方法里寫上如下代碼: 在工程目錄下新建一個generator.xml文件: 最後的table標簽是自己資料庫中表的名字;資料庫的連接信息需要自己修改 執行test類就會自動生成自己以上設置table標簽中數據中表的對應的實體類,dao層介面以及對應的mapper映 ...
  • 可變對象與不可變對象 要理解深拷貝和淺拷貝,首先要理解可變對象和不可變對象。 不可變對象:該對象所指向的記憶體中的值不能被改變,修改對象的值時,由於其指向的值不能被改變,因此實際上是在記憶體中重新開闢一個地址用來存儲新的值,然後將對象指向這個新值。本質上是兩個對象,賦值前後對象id發生了變化。pytho ...
  • 這篇博客記錄@InitBinder怎麼起作用、起什麼作用? 首先,該註解被解析的時機,是該匹配Controller的請求執行映射的方法之前; 同時 @InitBinder標註的方法執行是多次的,一次請求來就執行一次。 當某個Controller上的第一次請求由SpringMvc前端控制器匹配到該Co ...
  • 首先,作業要求概括如下: 根據首碼表達式文法,實現statements() 和expression() 兩個函數。 並且要求使得語義分析在完成分析首碼表達式並輸出中間代碼的同時,也能夠將首碼表達式翻譯為中綴表達式, 且要求翻譯後的中綴表達式中儘可能少用括弧。 舉例如下: 輸入"+ a * b c;" ...
  • 在未來某個指定的時間點或者經過一段時間延遲後執行某個事件,這時候就需要用到定時器了。定時器的實現方式有很多種,今天總結最簡單的實現方式。java 1.3引入了定時器框架,用於在定時器上下文中控制線程的執行,其由類Timer和TimerTask構成。Timer適用於大規模併發調度定時任務,在內部,該類 ...
  • 1、使用Java EE 在eclipse 開發動態的Web工程(Java web項目)1)開發開發選項切換到JavaEE2)可以在Windows->show view中找到package explorer,並將其托拽到開發區的左邊3)在servers面板中新建tomcat伺服器,一定要關聯到tomc ...
  • ...
  • 1. nginx+lua學習 1.1. 網關架構 1.2. nginx命令和信號控制 1. nginx s stop 快速關閉,不管有沒有正在處理的請求 nginx s quit 優雅關閉方式,推出前完成已經接受的連接請求 2. nginx c nginx配置文件地址 啟動 3. nginx s r ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...