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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...