天天看點

Java進階 | 泛型機制與反射原理

泛型即可以了解為把資料類型作為參數,即參數化類型,用來提高代碼的安全性,靈活性,避免類型轉換;代碼簡潔明了,同時對于程式的可擴充性起到至關重要的作用。反射機制可以在程式運作時擷取類的完整結構資訊,并且可以動态的操作屬性和方法等。對于反射機制的了解,必須要對類編譯和JVM加載,運作時資料區有清楚的認識,這塊内容可以移步JVM系列的文章。

泛型在Java中的應用非常廣泛,最常見則是在集合容器中,先看下基礎用法:

聲明一個map類型的容器,并且明确限定key和value的類型:分别為Integer,String,這樣顯然不能展現特别之處,可以對比下面的用法:

在不指定類型的情況下,鍵值對都預設為Object類型,這樣的容器在使用的時候要時刻注意不同的key類型和取出的value值類型,并且value要做類型轉換,相比之下泛型機制就很有必要。

可以看下Map接口的定義:

在Map接口中,<code>&lt;K,V&gt;</code>顯然沒有指定明确的類型,隻是起到類型傳遞的作用,即K是key的類型,V是value的類型,在上面的源碼中描述的很清楚,結合上面案例,在Map對象聲明的時候可以明确指定<code>&lt;K,V&gt;</code>的類型,也可以預設為Object類型。

泛型即可以了解為把資料類型作為參數,即參數化類型,用來提高代碼的安全性,靈活性,避免類型轉換;代碼簡潔明了,同時對于程式的可擴充性起到至關重要的作用。

首先設計一個簡單的頂層接口,隻定義一個callBack方法,和對出入參數的簡單邏輯設定,這種設計在Java的源碼設計中随處可見,例如上面的集合體系:

為了實作具體的業務,再基于頂層接口向下做擴充,這裡聲明兩個擴充接口,作為具體業務類的接口:

這樣可以通過擴充接口去設計具體的業務類,提高程式的靈活可擴充性:

通過上面這個案例,可以清楚的感覺到泛型機制的靈活和強大。

泛型雖然可以使用在類,接口,方法,參數等各個地方,但是其限制能力是在代碼的編譯期:

編譯過程中,會對泛型合法性作校驗,校驗成功編譯的class檔案沒有泛型資訊,即泛型擦除掉,通過一個簡單的指令檢視編譯後的檔案:

Java進階 | 泛型機制與反射原理

當然這也會帶來安全問題:

這裡即通過反射的機制,繞開泛型,在map中放入程式語義上的非法值類型,在運作過程中擷取值的時候才抛出類型轉換異常。

反射機制可以在程式運作時擷取類的完整結構資訊,并且可以動态的操作屬性和方法等。

Java進階 | 泛型機制與反射原理

對于反射機制的了解,必須要對類編譯和JVM加載,運作時資料區有清楚的認識,這塊内容可以移步JVM系列的文章。

通過運作時動态擷取類的結構,然後動态的建立對象并操作屬性和方法,這種方式在實際開發中并不多用,這樣很明顯會消耗JVM資源,并且會忽略一些封裝導緻安全問題,這在上面【1】中已經案例說明了。

java.lang.Class:Class類

java.lang.reflect.Constructor:構造器

java.lang.reflect.Field:屬性

java.lang.reflect.Method:方法

API之Class對象

擷取目标類型的Class對象常見方式,通過Class對象再擷取相關結構資訊,進而操作或者通路:

輸出結果:

這裡有個注意點:通過Class對象的<code>newInstance()</code>方法,即基于User類的無參構造器,首先要求User類具有無參構造方法。

API之Constructor構造器

Class對象讀取構造方法,可以分别獲得全部構造方法,不同修飾類型的構造方法,或者根據構造參數類型指定擷取:

這裡需要注意的是,通過調用<code>setAccessible(Boolean.TRUE)</code>方法,可以基于私有構造方法建立對象,這裡明顯違背了Java的基本設計原則,破壞代碼的安全性。

API之Field屬性

Field保證成員變量的屬性,修飾符,值管理等相關操作:

注意這裡擷取Type類型資訊,在有些特定的業務場景下還是十分有用的。

API之Method方法

注意這裡對方法的擷取遠遠不止類本身定義的,包括從父類繼承的,和Java基礎Object類中的。

閱讀标簽

【Java基礎】【設計模式】【結構與算法】【Linux系統】【資料庫】

【分布式架構】【微服務】【大資料元件】【SpringBoot進階】【Spring&amp;Boot基礎】

【資料分析】【技術導圖】【 職場】