深刻淺出泛型,框架設計的基礎

  泛型在 Java 5 出現,實現了參數化類型,主要做用是使得類或接口更加通用。好比 Java 中的容器類,經過泛型實現了對各類類型的兼容,成爲極其通用的類庫。若是咱們要設計本身的框架,泛型基本上已經算是標配了。設計模式

類使用泛型安全

在類型上使用泛型,很是簡單,只須要使用 <> 聲明類型便可。以下 TestDO 類型聲明瞭泛型 T,並使用 T 聲明瞭字段 value。這樣作的好處即是,TestDO 能夠承載各類各樣的數據,而不用定義具體的類型。這也說明了,泛型不只能夠用於類型,也能夠用於字段或變量。性能優化

Java 在運行時對泛型進行擦除,運行時全部的泛型類型都退化爲 Object 類型。這樣的設計主要是爲了保證對 Java 5 以前的代碼進行兼容。儘管如此,使用泛型後,在編譯階段仍會對類型進行檢查,確保使用泛型的地方都是類型正確的。數據結構

@Getter架構

@Setter併發

public框架

classTestDO{Tvalue;}分佈式

以下代碼中,SubTestDO 傳遞給父類的具體類型爲默認類型 Object 類型,而SubTestDOSimple 傳遞給父類的具體類型則是 String 類型。這兩個子類都對泛型作了具體化,是泛型類型的常規使用。ide

/** 無泛型,即爲 Object */微服務

classSubTestDOextendsTestDO{}

/** 繼承時指定父類實際類型 */

classSubTestDOSimpleextendsTestDO{}

在定義 TestDO 的子類時,子類也可使用泛型。子類的泛型能夠獨自使用,也能夠傳遞給父類。如 SubTestDOTrans 定義了泛型 T 並把 T 傳遞給父類,而 SubTestDOComp 定義了泛型 T 要求必須是 HashMap 的子類,但傳遞給父類的泛型變成了 List。

/** 繼承時傳遞泛型 */classSubTestDOTransextendsTestDO{}

/** 繼承時傳遞複雜類型 */

publicclassSubTestDOCompextendsSubTestDOTrans>{}

接口使用泛型能夠定義適用於各類場景的通用接口,在設計模式中具備重要做用。如工廠模式就能夠實現根據返回值類型自動轉型,涉及到方法的泛型,在後文給出解釋。

以下代碼中,IGeneric 接口使用了泛型,並用泛型定義了方法。如此定義後,IGeneric 接口成爲了一個通用接口,方法的入參類型與實現類或子接口對接口的定義保持一致,大大提升了通用性。接口能夠繼承,在繼承中泛型能夠有形式的變化,並定義新的泛型。

/** 普通泛型接口,泛型 T */

publicinterfaceIGeneric{publicvoidtest(Tt);}

ISubGenericSimple 接口在繼承接口時,直接肯定了泛型的類型爲 String 類型,ISubGenericSimple 接口退化爲一個普通接口,子接口或實現類再也不須要對泛型進行處理。這是經過子接口對泛型進行具體化,也是架構設計中的經常使用手段。

/** 繼承時指定泛型 */

interfaceISubGenericSimpleextendsIGeneric{}

ISubGenericTrans 接口一樣使用了泛型,並把泛型傳遞給父接口。這種通常用於父接口的擴展,在子接口中能夠定義一些特有方法,同時又徹底兼容父接口的引用。

/** 繼承時傳遞泛型 */

interfaceISubGenericTransextendsIGeneric{}

ISubGenericSubType 接口在泛型定義中,明確聲明瞭泛型類型的範圍必須是 TestDO 及其子類,這也是對父接口的一種退化,縮小了泛型的範圍。這種狀況通常用於比較複雜的接口體系,父接口完成頂層定義,多個子接口進行類型具體化實現分層設計。

/** 繼承時指定泛型爲特定類型的子類 */

interfaceISubGenericSubTypeextendsIGeneric{}

ISubGenericCompType 接口使用了一個相對複雜的泛型定義。子接口定義了泛型 T,而傳遞給父接口的泛型變成了 TestDO。這種接口繼承方式在大型軟件架構中有比較普遍的應用,通俗理解爲父接口使用容器但元素類型爲泛型,而子接口定義容器的元素類型,在組件化繼承體系有比較明確的應用。這樣的繼承關係,能夠確保父接口完成容器的操做方法而不用關心具體元素類型,子接口能夠有少量定製邏輯甚至能夠不定義方法。

/** 繼承時指定複雜的泛型類型 */

interfaceISubGenericCompTypeextendsIGeneric>{}

方法使用泛型

方法也可使用泛型,而且有很大的現實意義,在工廠模式、建造器模式中均可以使用泛型方法。

toString 方法定義了一個簡單的泛型方法。在訪問權限和返回值類型之間使用 聲明泛型類型爲 T,而後把入參類型設置爲 T。在這個泛型方法中,入參類型能夠是任何類型,這個通用方法能夠在任何場景下使用。

/** 泛型 T 僅用於入參 */

staticStringtoString(Tt){returnnull;}

getInstance 方法不但在入參中使用了泛型,也把這個泛型做爲返回值的類型。相似 getInstance 方法的定義有不少,好比 Spring 的 getBean 方法就重載了這種形式的應用。在確保方法通用性的基礎上,也保證了返回值類型與入參的一致性。

/** 返回 T 類型 */

staticTgetInstance(Classclazz)throwsException{returnclazz.newInstance();}

getTestDO 方法是另外一種形式的泛型方法,這裏定義了返回值的泛型必須是 TestDO 及其子類。這種泛型方法經常使用於繼承體系明確,須要對類型進行向下轉型的場景。在方法內部須要對返回值進行強制轉型,若是類型不匹配便會拋出類型轉換異常,所以要求調用者必須瞭解方法的定義和使用要求。

/** 返回 TestDO 的子類類型 */

staticTgetTestDO(){return(T)newSubTestDOTrans<>();}

下面 getTestDO 的重載方法對泛型作了必定程度的退化,雖然定義了泛型 T,但這個泛型僅僅是容器的元素類型,方法的返回值是 TestDO。這種類型的泛型方法可用於複雜對象的組裝,比上面的重載方法更安全。在這裏順便給你們推薦一個架構交流羣:617434785,裏面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化這些成爲架構師必備的知識體系。還能領取免費的學習資源。相信對於已經工做和遇到技術瓶頸的碼友,在這個羣裏會有你須要的內容。

/** 返回 TestDO 類型 */

staticTestDOgetTestDO(Tv){TestDOtestDO=newTestDO<>();testDO.setValue(v);returntestDO;}

泛型擦除

前面提到過 Java 會在運行時對泛型進行擦除,也就是抹去泛型信息,所有變爲 Object 類型。所以,在運行時,TestDO 與 TestDO 在類型上並無多大區別。雖然實際存儲的 value 對象的真正類型是不一樣的,可是都會當作 Object 進行處理。也是由於擦除,引用類型對象時,咱們可使用 TestDO.class 卻不能使用 TestDO.class。

泛型通配

前面的泛型都明確了泛型的類型爲 T,並使用 T 定義字段、參數和返回值。在對具體類型依賴不那麼強烈的狀況下,好比,咱們僅僅是要求類型是某個類型的子類便可,最終處理時使用父類類型而不依賴於子類類型,則可使用通配符 ? 來定義泛型。

在以下代碼中,set1 的元素類型爲 TestDO 的子類。咱們只關心 Set 中的元素是 TestDO 類型便可,並不關心實際元素是 SubTestDOSimple 仍是 SubTestDOComp。而 set2 的元素類型能夠是任何類型。

Setset1=newHashSet<>();

Setset2=newHashSet<>();

泛型逆變

前面提到的泛型定義用到了 extends 來肯定泛型的邊界,要求其必須是某個類型的子類。實際上,泛型還支持向上肯定邊界,也就是泛型爲某個類型的父類。在以下代碼中,定義了 Set 的元素類型爲 SubTestDOComp 的父類。

SettestDOs=newHashSet<>();

使用泛型建立類型安全的分層結構

在架構設計中,經常涉及複雜的數據結構與設計分層,使用泛型對類型進行定義和約束,能夠確保抽象層的設計有足夠的通用性,同時最終產生的具體類型依然保持正確的數據類型。

好比在一個架構設計中,可能會分爲抽象層、基礎層、實現層三個層次。

在抽象層定義極度抽象的業務流程,須要這裏的類型定義都具備較好的通用性,這時能夠大量使用泛型和抽象類,只定義流程而不涉及具體類型。

在基礎層定義一些基礎服務,可能會在 core 層類型的基礎上作必定程度的擴展,封裝出多個適用範圍不一樣的業務處理流程。在這一層,也會使用泛型,但會更加具體。

而在實現層不會再使用泛型,而是給出真正的類型,並實現具體的邏輯。

/** 抽象層,指定泛型 K,V */

abstractclassAbstractGenericImplimplementsIGeneric>{publicMapmap=newHashMap<>();}

/** 基礎層,繼承 AbstractGenericImpl */

classBasicGenericImplextendsAbstractGenericImpl>{@Overridepublicvoidtest(Map>data){map.putAll(data);}}

/** 實現層,繼承 BasicGenericImpl */

publicclassComplexGenericImplextendsBasicGenericImpl{@Overridepublicvoidtest(Map>data){super.test(data);}}

總結

泛型在當今時代的 Java 開發中已經被普遍應用,如 Spring、MyBatis、Dubbo 等開源框架都使用了大量泛型來實現通用功能。在開發中,既要掌握開源框架的泛型使用方法,也要熟悉泛型的定義和使用,並對泛型有足夠深入的理解。若是你要開發本身的框架,那麼泛型必定是標配,須要好好理解和掌握。

相關文章
相關標籤/搜索