Java 泛型T,E,K,V,?,傻傻分不清? - IT人

文章推薦指數: 80 %
投票人數:10人

Date; import java.util.List; public class Generic { private T t; public void set(T t) { this.t = t; } public T get() { return t; } ... Togglenavigation IT人 IT人 Java泛型T,E,K,V,?,傻傻分不清? ZNineSun發表於 2021-01-01 Java 1.前言2.泛型的好處3.泛型中萬用字元 3.1?無界萬用字元3.2上界萬用字元3.3下界萬用字元4.?和T的區別 4.1區別1:通過T來確保泛型引數的一致性4.2區別2:型別引數可以多重限定而萬用字元不行4.3區別3:萬用字元可以使用超類限定而型別引數不行5.Class和Class>區別 1.前言 Java泛型(generics)是JDK5中引入的一個新特性,泛型提供了編譯時型別安全檢測機制,該機制允許開發者在編譯時檢測到非法的型別。

泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。

2.泛型的好處 在不使用泛型時,通過對型別Object的引用來實現引數的“任意化”,“任意化”帶來的缺點是要做顯式的強制型別轉換,但是這種轉換是要求開發者對實際引數型別可以預知的情況下進行的。

對於強制型別轉換錯誤的情況,編譯器可能不提示錯誤,在執行的時候才出現異常,這是本身就是一個安全隱患。

那麼泛型的好處就是在編譯的時候能夠檢查型別安全,並且所有的強制轉換都是自動和隱式的。

importjava.util.Date; importjava.util.List; publicclassGeneric{ privateTt; publicvoidset(Tt){ this.t=t; } publicTget(){ returnt; } publicstaticvoidmain(String[]args){ //這種宣告方式不需要強制轉換 Genericgeneric1=newGeneric(); generic1.set(newTbInfo(1,newDate())); TbInfotb=generic1.get(); System.out.println(tb.toString()); //這種宣告方式需要強制轉換 Genericgeneric2=newGeneric(); generic2.set(newTbInfo(1,newDate())); TbInfotb2=(TbInfo)generic2.get(); System.out.println(tb.toString()); } } 下面附上TbInfo程式碼: publicclassTbInfo{ privateIntegeragencyId; privateDatesendTime; publicTbInfo(IntegeragencyId,DatesendTime){ this.agencyId=agencyId; this.sendTime=sendTime; } publicStringgetAgencyContent(){ returnagencyContent; } publicvoidsetAgencyContent(StringagencyContent){ this.agencyContent=agencyContent; } publicIntegergetAgencyId(){ returnthis.agencyId; } publicvoidsetAgencyId(IntegeragencyId){ this.agencyId=agencyId; } publicDategetSendTime(){ returnthis.sendTime; } publicvoidsetSendTime(DatesendTime){ this.sendTime=sendTime; } /*ThiscodewasgeneratedbyTableGotools,mark2end.*/ @Override publicStringtoString(){ return"TbInfo{"+ ",agencyId="+agencyId+ ",sendTime="+sendTime+ '}'; } } 執行結果如下: 3.泛型中萬用字元 我們在定義泛型類,泛型方法,泛型介面的時候經常會碰見很多不同的萬用字元,比如T,E,K,V等等,這些萬用字元又都是什麼意思呢?常用的T,E,K,V,? 本質上這些個都是萬用字元,沒啥區別,只不過是編碼時的一種約定俗成的東西。

比如上述程式碼中的T,我們可以換成A-Z之間的任何一個字母都可以,並不會影響程式的正常執行,但是如果換成其他的字母代替T,在可讀性上可能會弱一些。

通常情況下,T,E,K,V,?是這樣約定的: ?表示不確定的java型別T(type)表示具體的一個java型別KV(keyvalue)分別代表java鍵值中的KeyValueE(element)代表Element 3.1?無界萬用字元 我們先看一個小的demo我有一個父類Animal和幾個子類,如狗、貓等,現在我需要一個動物的列表,我的第一個想法是像這樣的: ListlistAnimals 但是老闆的想法確實這樣的: ListlistAnimals 老闆為什麼想要使用萬用字元而不是簡單的泛型呢?萬用字元其實在宣告區域性變數時是沒有什麼意義的,但是當你為一個方法宣告一個引數時,它是非常重要的。

比如以下程式碼: publicclassGeneric01{ staticintcountLegs(Listanimals){ intretVal=0; for(Animalanimal:animals) { retVal+=animal.countLegs(); } returnretVal; } staticintcountLegs1(Listanimals){ intretVal=0; for(Animalanimal:animals) { retVal+=animal.countLegs(); } returnretVal; } publicstaticvoidmain(String[]args){ Listdogs=newArrayList<>(); //不會報錯 countLegs(dogs); //報錯 countLegs1(dogs); } } 其中Animal,Dog程式碼如下 Animal publicclassAnimal{ privateIntegerlegs; publicIntegercountLegs(){ returnlegs; } publicIntegergetLegs(){ returnlegs; } publicvoidsetLegs(Integerlegs){ this.legs=legs; } } Dog publicclassDogextendsAnimal{ @Override publicIntegercountLegs(){ returnsuper.countLegs(); } } 但是在我們實際上使用時根本不知道我們要傳入的引數到底是Dog還是Cat,所以對於不確定或者不關心實際要操作的型別,可以使用無限制萬用字元(尖括號裡一個問號,即>)表示可以持有任何型別。

像countLegs方法中,限定了上界,但是不關心具體型別是什麼,所以對於傳入的Animal的所有子類都可以支援,並且不會報錯。

而countLegs1就不行。

於是我們引出了上界萬用字元 3.2上界萬用字元 上界:用extends關鍵字宣告,表示引數化的型別可能是所指定的型別,或者是此型別的子類。

在型別引數中使用extends表示這個泛型中的引數必須是E或者E的子類,這樣有兩個好處: 如果傳入的型別不是E或者E的子類,編譯不成功泛型中可以使用E的方法,要不然還得強轉成E才能使用 型別引數列表中如果有多個型別引數上限,用逗號分開,比如: privateEtest(Karg1,Earg2){ Eresult=arg2; arg2.compareTo(arg1); //..... returnresult; } 3.3下界萬用字元 下界:用super進行宣告,表示引數化的型別可能是所指定的型別,或者是此型別的父型別,直至Object 在型別引數中使用super表示這個泛型中的引數必須是E或者E的父類。

packagecom.ownplus.consumerown.test.generic; importjava.util.ArrayList; importjava.util.List; publicclassGeneric02{ privatevoidtest(Listdst,Listsrc){ for(Tt:src){ dst.add(t); } } publicstaticvoidmain(String[]args){ Listdogs=newArrayList<>(); for(inti=0;i<10;i++){ Dogdog=newDog(); dog.setLegs(i); dogs.add(dog); } Listanimals=newArrayList<>(); newGeneric02().test(animals,dogs); } } 我大致解釋一下這串程式碼: 首先我們傳入的引數是animals,dogs而Listdst表示泛型中的引數必須是T的父類,那麼這個T又是什麼呢我們接下來看第二個引數Listsrc,說明我們在使用test方法時,傳入的第二個引數的泛型型別就是T對應的型別現在我們回到main方法中,newGeneric02().test(animals,dogs)說明傳入的T的型別是dog型別,第一個引數對應的萬用字元?應該是T對應的父類,而我們傳入的第一個引數是animals,那就是說明?對應的型別是animal型別,符合?為T的父類這一規則 4.?和T的區別 //指定元素的集合只能是T型別 Listlist1=newLinkedList<>(); //指定元素的集合可以是任意型別,不過這種沒有任何意義,一般是方法中,只是為了說明用法 List>list2=newLinkedList<>(); ?和T都表示不確定的型別,區別在於我們可以對T進行操作,但是對?不行,比如如下這種: //可以 Tt=operate(); //不可以 ?car=operate(); T是一個確定的型別,通常用於泛型類和泛型方法的定義?是一個不確定的型別,通常用於泛型方法的呼叫程式碼和形參,不能用於定義類和泛型方法。

4.1區別1:通過T來確保泛型引數的一致性 //通過T來確保泛型引數的一致性 publicvoid test(Listdest,Listsrc) //萬用字元是不確定的,所以這個方法不能保證兩個List具有相同的元素型別 publicvoidtest(Listdest,Listsrc) 比如以下程式碼: publicclassGeneric03{ //可以保證兩個list元素的一致性 publicvoidtest0(Listdest,Listsrc){ } publicstaticvoidmain(String[]args){ Lista1=newLinkedList<>(); Lista2=newLinkedList<>(); newGeneric03().test0(a1,a2); } } 則會出現以下結果: 但是如果我們使用?萬用字元: publicclassGeneric03{ //不可以保證兩個list元素的一致性 publicvoidtest1(Listdest,Listsrc){ } publicstaticvoidmain(String[]args){ Lista1=newLinkedList<>(); Lista2=newLinkedList<>(); newGeneric03().test1(a1,a2); } } 我們會發現並沒有報錯 4.2區別2:型別引數可以多重限定而萬用字元不行 使用&符號設定多重邊界(MultiBounds),指定泛型型別T必須是MultiLimitInterfaceA和MultiLimitInterfaceB的共有子型別,此時變數t就具有了所有限定的方法和屬性。

對於萬用字元(?)來說,因為它不是一個確定的型別,所以不能進行多重限定。

4.3區別3:萬用字元可以使用超類限定而型別引數不行 型別引數T只具有一種型別限定方式:TextendsA但是萬用字元?可以進行兩種限定: ?extendsA ?superA 5.Class和Class>區別 前面介紹了?和T的區別,那麼對於,Class和Class>又有什麼區別呢?Class和Class>最常見的是在反射場景下的使用,這裡以用一段反射的程式碼來說明下: //通過反射的方式生成multiLimit物件,這裡比較明顯的是,我們需要使用強制型別轉換 MultiLimitmultiLimit=(MultiLimit) Class.forName("com.glmapper.bridge.boot.generic.MultiLimit").newInstance(); 對於上述程式碼,在執行期,如果反射的型別不是MultiLimit類,那麼一定會報java.lang.ClassCastException錯誤。

對於這種情況,則可以使用下面的程式碼來代替,使得在在編譯期就能直接檢查到型別的問題: Class在例項化的時候,T要替換成具體類。

Class>它是個通配泛型,?可以代表任何型別,所以主要用於宣告時的限制情況。

比如,我們可以這樣做申明: //可以 publicClass>clazz; //不可以,因為T需要指定型別 publicClassclazzT; 所以當不知道定宣告什麼型別的Class的時候可以定義一個Class>。

那如果也想publicClassclazzT這樣的話,就必須讓當前的類也指定T,即: publicclassTest3{ publicClass>clazz; //不會報錯 publicClassclazzT;} 相關文章 Java多執行緒 2022-06-24 Java Scala與Java的關係 2020-12-31 Java JavaScript的三種引入方式 2020-12-31 JavaScript 入門全棧Java程式設計師——課程介紹 2020-12-31 Java全棧程式設計師 Java-泛型 2022-03-03 Java Java條件表示式的優化 2021-01-01 Java 十四在IDE中新建一個動態WebJava工程時沒有JavaEE(Legacy)\webApplication? 2021-01-01 Java JavaFX——fxml檔案載入錯誤:[javafx.fxml.LoadException]解決方案之一 2021-01-01 Java 通過JavaScript如何做一個仿京東的跟隨圖片放大鏡效果?(附加註釋)簡單實用。

2021-01-01 JavaScript JavaFX:匯入ikonli-master2.0 2021-01-01 Java 利用Docker構建一個簡單的java開發編譯環境 2021-01-01 JavaDocker 如何在Java中將字串轉換為日期 2021-01-01 Java 基於java的企業車輛管理系統的設計與實現 2021-01-01 Java java學習之深入構造器 2021-01-01 Java java如何讓程式碼變得優雅——自定義註解 2021-01-01 Java 1.13-java單元測試junit 2021-01-01 Java File類的特點?如何建立File類物件?Java中如何操作檔案內容,什麼是Io流Io流如何讀取和寫入檔案?位元組緩衝流使用原則? 2021-01-01 Java 深入理解Java虛擬機器–閱讀class檔案的三種姿勢(連載2) 2021-01-01 Java java陣列的記憶體分析 2021-01-01 Java 公眾號傳送模板資訊java實現(主動傳送) 2021-01-01 Java 最新文章 CMR:印度手機遊戲調查報告 大咖說·圖書分享|HaaS物聯網裝置雲端一體開發框架 話實踐,行實幹,成實事:“巡禮”數字化的中國大地 構建安全程式碼防止供應鏈攻擊 【推薦閱讀】超有用的漏洞掃描工具合集! 國密SM演算法有哪些? 為什麼伺服器選擇Linux系統 ApacheFlinkML2.1.0釋出公告 乾貨|作為前端開發者如何邁向獨立開發者 助力開發者,全方位解讀APISIX測試案例 得物資料庫中介軟體平臺“彩虹橋”演進之路 KubeSphere3.3.0離線安裝教程



請為這篇文章評分?