本篇內(nèi)容介紹了“如何掌握J(rèn)ava泛型”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
創(chuàng)新互聯(lián)自2013年創(chuàng)立以來,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目網(wǎng)站制作、做網(wǎng)站網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元泌陽做網(wǎng)站,已為上家服務(wù),為泌陽各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:028-86922220
為什么要用泛型?
設(shè)想一個(gè)場(chǎng)景,我們希望用Java創(chuàng)建一個(gè)列表來存儲(chǔ)Integer;代碼可能會(huì)寫成這樣:
List list = new LinkedList(); list.add(new Integer(1)); Integer i = list.iterator().next();
令人驚訝的是,編譯器會(huì)提示最后一行。它不知道返回的數(shù)據(jù)類型是什么。因此,編譯器提示需要進(jìn)行顯式轉(zhuǎn)換:
Integer i = (Integer) list.iterator.next();
沒有任何約定可以保證列表的返回類型是整數(shù)。定義的列表可以包含任何對(duì)象。我們只知道我們是通過檢查上下文來檢索列表的。在查看類型時(shí),它只能保證它是一個(gè)Object,因此需要顯式轉(zhuǎn)換來確保類型是安全的。
這種轉(zhuǎn)換可能會(huì)令人感到聒噪,我們明明知道這個(gè)列表中的數(shù)據(jù)類型是整數(shù)。轉(zhuǎn)換的話,也把我們的代碼搞得亂七八糟。如果程序員在顯式轉(zhuǎn)換中出錯(cuò),則可能會(huì)導(dǎo)致拋出與 類型相關(guān)的運(yùn)行時(shí)錯(cuò)誤 。
如果程序員能夠表達(dá)他們使用特定類型的意圖,并且編譯器能夠確保這種類型的正確性,那么這將更加容易。
這就是泛型背后的核心思想。
我們將前面代碼段的第一行修改為:
Listlist = new LinkedList<>();
通過添加包含類型的菱形運(yùn)算符<>,我們將此列表的特化范圍縮小到 Integer 類型,即指定將保存在列表中的類型。編譯器可以在編譯時(shí)強(qiáng)制執(zhí)行該類型。
在較小的程序中,這看起來像是一個(gè)微不足道的添加。但是在較大的程序中,這可以增加顯著的健壯性并使程序更易于閱讀。
泛型方法
泛型方法是用單個(gè)方法聲明編寫的方法,可以用不同類型的參數(shù)調(diào)用。編譯器將確保所用類型的正確性。以下是泛型方法的一些屬性:
泛型方法在方法聲明的返回類型之前有一個(gè)類型參數(shù)(包裹類型的菱形運(yùn)算符)
類型參數(shù)可以有界(邊界將在本文后面解釋)
泛型方法可以具有不同的類型參數(shù),這些參數(shù)在方法簽名中用逗號(hào)分隔
泛型方法的方法體與普通方法一樣
定義將數(shù)組轉(zhuǎn)換為列表的泛型方法的示例:
publicList fromArrayToList(T[] a) { return Arrays.stream(a).collect(Collectors.toList()); }
在前面的示例中,方法聲明中的
public staticList fromArrayToList(T[] a, Function mapperFunction) { return Arrays.stream(a) .map(mapperFunction) .collect(Collectors.toList()); }
我們正在傳遞一個(gè)函數(shù),該函數(shù)將具有T類型元素的數(shù)組轉(zhuǎn)換為包含G類型元素的列表。例如,將 Integer 轉(zhuǎn)換為其 String 表示形式:
@Test public void givenArrayOfIntegers_thanListOfStringReturnedOK() { Integer[] intArray = {1, 2, 3, 4, 5}; ListstringList = Generics.fromArrayToList(intArray, Object::toString); assertThat(stringList, hasItems("1", "2", "3", "4", "5")); }
Oracle建議使用大寫字母表示泛型類型,并選擇更具描述性的字母來表示形式類型,例如在Java集合中,T 用于類型,K 表示鍵,V 表示值。
泛型邊界
如前所述,類型參數(shù)可以是有界的。有界意味著“限制”,我們可以限制方法可以接受的類型。
例如,可以指定一個(gè)方法接受一個(gè)類型及其所有子類(上限)或一個(gè)類型所有它的超類(下限)。
要聲明上界類型,我們?cè)陬愋秃竺媸褂藐P(guān)鍵字extends,后跟要使用的上限。例如:
publicList fromArrayToList(T[] a) { ... }
這里使用關(guān)鍵字extends表示類型 T 擴(kuò)展類的上限,或者實(shí)現(xiàn)接口的上限。
多個(gè)邊界
類型還可以有多個(gè)上界,如下所示:
如果 T 擴(kuò)展的類型之一是類(即Number),則必須將其放在邊界列表的第一位。否則,將導(dǎo)致編譯時(shí)錯(cuò)誤。
使用通配符
通配符在Java中用問號(hào)“?“ 表示,它們是用來指一種未知的類型。通配符在使用泛型時(shí)特別有用,可以用作參數(shù)類型,但首先要考慮的是一個(gè)重要的注釋。
眾所周知,Object是所有Java類的超類型,但是,Object的集合不是任何集合的超類型。(可能有點(diǎn)繞,大家好好細(xì)品一下)
例如,List 不是 List
這是為了防止在將異構(gòu)類型添加到同一集合時(shí)可能發(fā)生的沖突。
相同的規(guī)則適用于類型及其子類型的任何集合??纯催@個(gè)例子:
public static void paintAllBuildings(Listbuildings) { buildings.forEach(Building::paint); }
如果我們?cè)O(shè)想一個(gè)子類型Building,實(shí)例House,那么我們不能將此方法與House列表一起使用,即使House是Building的子類型。如果需要將此方法與類型構(gòu)建及其所有子類型一起使用,則有界通配符可以實(shí)現(xiàn)以下功能:
public static void paintAllBuildings(List extends Building> buildings) { ... }
現(xiàn)在,這個(gè)方法可以處理Building類型及其所有子類型。這稱為上界通配符,其中類型Building是上界。
通配符也可以使用下限指定,其中未知類型必須是指定類型的超類型。可以使用super關(guān)鍵字后跟特定類型來指定下限,例如,表示未知類型,它是 T(=T及其所有父類)的超類。
類型擦除
泛型被添加到Java中以確保類型安全,并確保泛型不會(huì)在運(yùn)行時(shí)造成開銷,編譯器在編譯時(shí)對(duì)泛型應(yīng)用一個(gè)名為type erasure的進(jìn)程。
類型擦除刪除所有類型參數(shù),并將其替換為它們的邊界,如果類型參數(shù)是無界的,則替換為Object。因此,編譯后的字節(jié)碼只包含普通的類、接口和方法,從而確保不會(huì)生成新的類型。在編譯時(shí)對(duì)Object類型也應(yīng)用了正確的強(qiáng)制轉(zhuǎn)換。以下是類型擦除的一個(gè)示例:
publicList genericMethod(List list) { return list.stream().collect(Collectors.toList()); }
使用類型擦除,無界類型T將替換為Object,如下所示:
// for illustration public List
如果類型是有界的,則在編譯時(shí)該類型將替換為綁定:
publicvoid genericMethod(T t) { ... }
編譯后會(huì)發(fā)生變化:
public void genericMethod(Building t) { ... }
泛型和原始數(shù)據(jù)類型
Java中泛型的一個(gè)限制是類型參數(shù)不能是基本類型
例如,以下內(nèi)容無法編譯:
Listlist = new ArrayList<>(); list.add(17);
為了理解原始數(shù)據(jù)類型為什么不起作用,只需記住 泛型是編譯時(shí)特性,這意味著類型將會(huì)被擦除,所有泛型類型都實(shí)現(xiàn)為 Object 類。舉一個(gè)例子,讓我們看看列表的 add 方法:
Listlist = new ArrayList<>(); list.add(17);
add 方法的聲明如下:
boolean add(E e);
并將被編譯為:
boolean add(Object e);
因此,類型參數(shù)必須可轉(zhuǎn)換為Object。由于基本類型不繼承自 Object,所以不能將它們用作類型參數(shù) 但是,Java為它們提供了裝箱類型,以及自動(dòng)裝箱和自動(dòng)拆箱:
Integer a = 17; int b = a;
因此,如果我們想創(chuàng)建一個(gè)可以保存整數(shù)的列表,我們可以使用包裝器:
Listlist = new ArrayList<>(); list.add(17); int first = list.get(0);
編譯后的代碼相當(dāng)于:
List list = new ArrayList<>(); list.add(Integer.valueOf(17)); int first = ((Integer) list.get(0)).intValue();
Java的未來版本可能允許泛型使用原始數(shù)據(jù)類型。Valhalla 工程旨在改進(jìn)處理泛型的方式。其思想是實(shí)現(xiàn)JEP 218中描述的泛型專門化.
“如何掌握J(rèn)ava泛型”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!