摘要:調(diào)用相當(dāng)于通過(guò)使用通配符,可以傳遞任何類型的對(duì)象,但也是有缺點(diǎn)的。使用通配符,賦值傳值的時(shí)候方便了,但是對(duì)泛型類中參數(shù)為泛型的方法起到了副作用。結(jié)論當(dāng)使用父界限定通配符時(shí),泛型類中返回值為泛型的方法不能使用。
數(shù)組 VS List
第一回合
數(shù)組類型為Object,可以存儲(chǔ)任意類型的對(duì)象,List集合同樣可以做到
Object[] obj = new Object[1]; List list = new ArrayList();
第二回合
數(shù)組類型可以為Integer類型,專門存儲(chǔ)Integer類型對(duì)象,List集合同樣也可以
Integer[] integer = new Integer[1]; Listlist = new ArrayList<>();
決勝局
前兩局雙方打成平手,最后的決勝局。先看數(shù)組代碼:
Object[] obj = new Integer[2]; obj[0] = 52021; //編譯期OK,運(yùn)行期就掛了 obj[1] = "Full of confidence";
上面的代碼在運(yùn)行期會(huì)拋出異常:java.lang.ArrayStoreException: java.lang.String
List集合代碼:
//編譯期報(bào)錯(cuò) List
最終結(jié)果,List集合更勝一籌,因?yàn)樗鼙M早的發(fā)現(xiàn)錯(cuò)誤。
分析最后一戰(zhàn)中數(shù)組的代碼:
在編譯期間,編譯器不會(huì)檢查這樣的賦值,編譯器覺(jué)得它是合理的,因?yàn)楦割愐弥赶蜃宇悓?duì)象,沒(méi)有任何問(wèn)題。賦值時(shí),將字符串存儲(chǔ)在一個(gè)Object類型的數(shù)組中也說(shuō)的過(guò)去,但在運(yùn)行時(shí),發(fā)現(xiàn)明明內(nèi)存分配的是存儲(chǔ)整型類型對(duì)象的格子,里面卻放著字符串類型的對(duì)象,自然會(huì)報(bào)錯(cuò)了。
分析最后一戰(zhàn)中List集合的代碼:
由于運(yùn)用了泛型,在編譯期就發(fā)現(xiàn)了錯(cuò)誤,避免了將問(wèn)題帶到運(yùn)行時(shí)。
思考個(gè)問(wèn)題,如果代碼在編譯期沒(méi)有報(bào)錯(cuò)會(huì)發(fā)生什么?
在編譯期沒(méi)有報(bào)錯(cuò)并且在運(yùn)行期會(huì)將泛型擦除,全部變?yōu)榱薕bject類型。所以執(zhí)行obj.add(new Integer(1))也是可以的。如果真是這樣的話,那么泛型還有什么存在的意義呢?所以這種假設(shè)是不存在的,所以會(huì)在編譯期報(bào)錯(cuò)。編譯期報(bào)錯(cuò)的原因就是在使用泛型時(shí),泛型的引用和創(chuàng)建兩端,給出的泛型變量不相同,所以在使用泛型時(shí),泛型的引用和創(chuàng)建兩端,給出的泛型變量必須相同。
通配符通配符只能出現(xiàn)在等號(hào)左面,不能出現(xiàn)在new的一邊。
List> list = new ArrayList
List extends Number> obj1 = new ArrayList
List super Integer> obj = new ArrayList
public void foo() { Listlist = new ArrayList (); //foo2(list); //編譯期報(bào)錯(cuò) foo3(list); //正常編譯 } public void foo2(List
上面代碼中,調(diào)用foo2方法編譯期報(bào)錯(cuò)原因:泛型的引用和創(chuàng)建兩端,給出的泛型變量不一致,相當(dāng)于:List
public void foo() { Listlist = new ArrayList (); List strList = new ArrayList (); foo3(list); //正常編譯 } public void foo3(List list) { //TODO }
現(xiàn)在希望將strList作為參數(shù)調(diào)用foo3方法,這時(shí)就想到了方法的重載,所以定義了一個(gè)重載的方法。public void foo3(List
定義完成后,竟然報(bào)錯(cuò)了,并且foo3(List
這里無(wú)法使用foo3方法重載,除非定義不同名字的方法。除了定義不同名字的方法之外,還可以使用通配符。
public void foo() { Listlist = new ArrayList (); List strList = new ArrayList (); foo3(list); foo3(strList); } public void foo3(List> list) { //TODO }
調(diào)用foo3(strList);相當(dāng)于:
List> list = new ArrayList
通過(guò)使用通配符,foo3(List> list);可以傳遞任何類型的對(duì)象,但也是有缺點(diǎn)的。使用通配符,賦值/傳值的時(shí)候方便了,但是對(duì)泛型類中參數(shù)為泛型的方法起到了副作用。如何理解泛型類中參數(shù)為泛型的方法起到了副作用這句話呢?結(jié)合代碼來(lái)看
/** * 使用 ? 通配符 */ public void foo3(List> list) { //list.add(1); //編譯期報(bào)錯(cuò),起到了副作用 //list.add("hello"); //編譯期報(bào)錯(cuò),起到了副作用 Object o = list.get(0); //其實(shí)也是作廢的,只是由于Object是所有類的父類,所以這里不會(huì)報(bào)錯(cuò)。 }
List定義:接口List
子界限定:? extends Number
解釋:? 是 Number 類型或者是 Number 的子類型
缺點(diǎn):參數(shù)為泛型的方法不能使用
public void foo4() { ListintList = new ArrayList (); List longList = new ArrayList (); foo5(intList); //Integer是Number的子類型 foo5(longList); //Long是Number的子類型 } public void foo5(List extends Number> list) { //list.add(1); //編譯期報(bào)錯(cuò) //list.add(2L); //編譯期報(bào)錯(cuò) }
分析以上代碼:
foo5(intList);相當(dāng)于List extends Number> list = intList;賦值操作,這是沒(méi)有問(wèn)題的;list.add(1);則會(huì)在編譯期報(bào)錯(cuò)。
list定義的類型是List extends Number>,它帶有泛型,而add方法的參數(shù)也是泛型類型,符合:泛型類中參數(shù)為泛型的方法起到了副作用這個(gè)結(jié)論。所以調(diào)用add編譯期報(bào)錯(cuò)。
想想看,如果list.add(1);不報(bào)錯(cuò):
foo4中調(diào)用了foo5(longList);相當(dāng)于List extends Number> list = new ArrayList
結(jié)論:當(dāng)使用子界限定通配符時(shí),泛型類中參數(shù)為泛型的方法不能使用。
也許有人會(huì)問(wèn),這樣做是否可以確定類型?
List extends Number> list = new ArrayList(); list.add(1); //編譯期報(bào)錯(cuò)
很遺憾,這樣也是不行的。? 仍然代表了不確定性,所以編譯器壓根就是將這種方式的方法的參數(shù)類型是泛型的全部廢掉了。
add不能用,那賦值操作后可從中取值嗎?答案是肯定的。
Number number = list.get(0);
分析以上代碼:
無(wú)論返回什么值,總歸都是Number類型的,這是可以確定的,所以可以用Number接收返回值為泛型的方法。當(dāng)然,這里說(shuō)沒(méi)有問(wèn)題也只能是Number類型或者是Object類型接收,別的類型是不可以的。
結(jié)論:當(dāng)使用子界限定通配符時(shí),泛型類中返回值為泛型的方法可以使用。
父界限定父界限定:? super Integer
解釋:? 是Integer類型或者是Integer的父類型
缺點(diǎn):返回值為泛型的方法不能使用
public void foo6() { ListnumList = new ArrayList (); List intList = new ArrayList (); foo7(numList); //Number是Integer的父類型 foo7(intList); //Integer是本身 } public void foo7(List super Integer> list) { list.add(1); }
分析以上代碼:
list.add(1);不會(huì)報(bào)錯(cuò),因?yàn)轭愋涂梢源_定。
如果List super Integer> list的賦值是List super Integer> list = new ArrayList
如果List super Integer> list的賦值是List super Integer> list = new ArrayList
如果List super Integer> list的賦值是List super Integer> list = new ArrayList也沒(méi)問(wèn)題
所以只要add(1)就肯定沒(méi)有問(wèn)題,就是說(shuō)add(1),這個(gè)實(shí)參1,符合任何一種情況。
結(jié)論:當(dāng)使用父界限定通配符時(shí),泛型類中參數(shù)為泛型的方法可以使用。
public void foo7(List super Integer> list) { list.add(1); Object obj = list.get(0); }
Object obj = list.get(0);這句話沒(méi)有報(bào)錯(cuò),但其實(shí)是作廢的,只是由于Object是所有類的父類,所以才可以這么用。
結(jié)論:當(dāng)使用父界限定通配符時(shí),泛型類中返回值為泛型的方法不能使用。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/73385.html
摘要:泛型之上界下界通配符本教程是為編寫的。這是在使用泛型編程時(shí)一個(gè)常見(jiàn)的誤解,也是一個(gè)需要學(xué)習(xí)的重要概念。通配符使用指南學(xué)習(xí)使用泛型編程時(shí),更令人困惑的一個(gè)方面是確定何時(shí)使用上限有界通配符以及何時(shí)使用下限有界通配符。 Java 泛型之上界下界通配符 本Java教程是為JDK 8編寫的。本頁(yè)描述的示例和實(shí)踐沒(méi)有利用后續(xù)版本中引入的改進(jìn)。 泛型,繼承和子類 如你所知,只要類型兼容,就可以將一種...
摘要:泛型類在類的申明時(shí)指定參數(shù),即構(gòu)成了泛型類。換句話說(shuō),泛型類可以看成普通類的工廠。的作用就是指明泛型的具體類型,而類型的變量,可以用來(lái)創(chuàng)建泛型類的對(duì)象。只有聲明了的方法才是泛型方法,泛型類中的使用了泛型的成員方法并不是泛型方法。 什么是泛型? 泛型是JDK 1.5的一項(xiàng)新特性,它的本質(zhì)是參數(shù)化類型(Parameterized Type)的應(yīng)用,也就是說(shuō)所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù),...
摘要:泛型類泛型類和普通類的區(qū)別就是類定義時(shí),在類名后加上泛型聲明。泛型類的內(nèi)部成員方法就可以使用聲明的參數(shù)類型。 泛型是JDK 1.5的一項(xiàng)新特性,它的本質(zhì)是參數(shù)化類型(Parameterized Type),即所操作的數(shù)據(jù)類型在定義時(shí)被指定為一個(gè)參數(shù)。當(dāng)我們使用的時(shí)候給這個(gè)參數(shù)指定不同的對(duì)象類型,就可以處理不同的對(duì)象。這種參數(shù)類型可以用在類、接口和方法的創(chuàng)建中,分別稱為泛型類、泛型接口和...
閱讀 3889·2021-09-27 13:36
閱讀 4612·2021-09-22 15:12
閱讀 3070·2021-09-13 10:29
閱讀 1840·2021-09-10 10:50
閱讀 2371·2021-09-03 10:43
閱讀 528·2019-08-29 17:10
閱讀 451·2019-08-26 13:52
閱讀 3263·2019-08-23 14:37