摘要:調用相當于通過使用通配符,可以傳遞任何類型的對象,但也是有缺點的。使用通配符,賦值傳值的時候方便了,但是對泛型類中參數為泛型的方法起到了副作用。結論當使用父界限定通配符時,泛型類中返回值為泛型的方法不能使用。
數組 VS List
第一回合
數組類型為Object,可以存儲任意類型的對象,List集合同樣可以做到
Object[] obj = new Object[1]; List list = new ArrayList();
第二回合
數組類型可以為Integer類型,專門存儲Integer類型對象,List集合同樣也可以
Integer[] integer = new Integer[1]; Listlist = new ArrayList<>();
決勝局
前兩局雙方打成平手,最后的決勝局。先看數組代碼:
Object[] obj = new Integer[2]; obj[0] = 52021; //編譯期OK,運行期就掛了 obj[1] = "Full of confidence";
上面的代碼在運行期會拋出異常:java.lang.ArrayStoreException: java.lang.String
List集合代碼:
//編譯期報錯 List
最終結果,List集合更勝一籌,因為它能盡早的發現錯誤。
分析最后一戰中數組的代碼:
在編譯期間,編譯器不會檢查這樣的賦值,編譯器覺得它是合理的,因為父類引用指向子類對象,沒有任何問題。賦值時,將字符串存儲在一個Object類型的數組中也說的過去,但在運行時,發現明明內存分配的是存儲整型類型對象的格子,里面卻放著字符串類型的對象,自然會報錯了。
分析最后一戰中List集合的代碼:
由于運用了泛型,在編譯期就發現了錯誤,避免了將問題帶到運行時。
思考個問題,如果代碼在編譯期沒有報錯會發生什么?
在編譯期沒有報錯并且在運行期會將泛型擦除,全部變為了Object類型。所以執行obj.add(new Integer(1))也是可以的。如果真是這樣的話,那么泛型還有什么存在的意義呢?所以這種假設是不存在的,所以會在編譯期報錯。編譯期報錯的原因就是在使用泛型時,泛型的引用和創建兩端,給出的泛型變量不相同,所以在使用泛型時,泛型的引用和創建兩端,給出的泛型變量必須相同。
通配符通配符只能出現在等號左面,不能出現在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); //編譯期報錯 foo3(list); //正常編譯 } public void foo2(List
上面代碼中,調用foo2方法編譯期報錯原因:泛型的引用和創建兩端,給出的泛型變量不一致,相當于:List
public void foo() { Listlist = new ArrayList (); List strList = new ArrayList (); foo3(list); //正常編譯 } public void foo3(List list) { //TODO }
現在希望將strList作為參數調用foo3方法,這時就想到了方法的重載,所以定義了一個重載的方法。public void foo3(List
定義完成后,竟然報錯了,并且foo3(List
這里無法使用foo3方法重載,除非定義不同名字的方法。除了定義不同名字的方法之外,還可以使用通配符。
public void foo() { Listlist = new ArrayList (); List strList = new ArrayList (); foo3(list); foo3(strList); } public void foo3(List> list) { //TODO }
調用foo3(strList);相當于:
List> list = new ArrayList
通過使用通配符,foo3(List> list);可以傳遞任何類型的對象,但也是有缺點的。使用通配符,賦值/傳值的時候方便了,但是對泛型類中參數為泛型的方法起到了副作用。如何理解泛型類中參數為泛型的方法起到了副作用這句話呢?結合代碼來看
/** * 使用 ? 通配符 */ public void foo3(List> list) { //list.add(1); //編譯期報錯,起到了副作用 //list.add("hello"); //編譯期報錯,起到了副作用 Object o = list.get(0); //其實也是作廢的,只是由于Object是所有類的父類,所以這里不會報錯。 }
List定義:接口List
子界限定:? extends Number
解釋:? 是 Number 類型或者是 Number 的子類型
缺點:參數為泛型的方法不能使用
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); //編譯期報錯 //list.add(2L); //編譯期報錯 }
分析以上代碼:
foo5(intList);相當于List extends Number> list = intList;賦值操作,這是沒有問題的;list.add(1);則會在編譯期報錯。
list定義的類型是List extends Number>,它帶有泛型,而add方法的參數也是泛型類型,符合:泛型類中參數為泛型的方法起到了副作用這個結論。所以調用add編譯期報錯。
想想看,如果list.add(1);不報錯:
foo4中調用了foo5(longList);相當于List extends Number> list = new ArrayList
結論:當使用子界限定通配符時,泛型類中參數為泛型的方法不能使用。
也許有人會問,這樣做是否可以確定類型?
List extends Number> list = new ArrayList(); list.add(1); //編譯期報錯
很遺憾,這樣也是不行的。? 仍然代表了不確定性,所以編譯器壓根就是將這種方式的方法的參數類型是泛型的全部廢掉了。
add不能用,那賦值操作后可從中取值嗎?答案是肯定的。
Number number = list.get(0);
分析以上代碼:
無論返回什么值,總歸都是Number類型的,這是可以確定的,所以可以用Number接收返回值為泛型的方法。當然,這里說沒有問題也只能是Number類型或者是Object類型接收,別的類型是不可以的。
結論:當使用子界限定通配符時,泛型類中返回值為泛型的方法可以使用。
父界限定父界限定:? super Integer
解釋:? 是Integer類型或者是Integer的父類型
缺點:返回值為泛型的方法不能使用
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);不會報錯,因為類型可以確定。
如果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也沒問題
所以只要add(1)就肯定沒有問題,就是說add(1),這個實參1,符合任何一種情況。
結論:當使用父界限定通配符時,泛型類中參數為泛型的方法可以使用。
public void foo7(List super Integer> list) { list.add(1); Object obj = list.get(0); }
Object obj = list.get(0);這句話沒有報錯,但其實是作廢的,只是由于Object是所有類的父類,所以才可以這么用。
結論:當使用父界限定通配符時,泛型類中返回值為泛型的方法不能使用。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73385.html
摘要:泛型之上界下界通配符本教程是為編寫的。這是在使用泛型編程時一個常見的誤解,也是一個需要學習的重要概念。通配符使用指南學習使用泛型編程時,更令人困惑的一個方面是確定何時使用上限有界通配符以及何時使用下限有界通配符。 Java 泛型之上界下界通配符 本Java教程是為JDK 8編寫的。本頁描述的示例和實踐沒有利用后續版本中引入的改進。 泛型,繼承和子類 如你所知,只要類型兼容,就可以將一種...
摘要:泛型類在類的申明時指定參數,即構成了泛型類。換句話說,泛型類可以看成普通類的工廠。的作用就是指明泛型的具體類型,而類型的變量,可以用來創建泛型類的對象。只有聲明了的方法才是泛型方法,泛型類中的使用了泛型的成員方法并不是泛型方法。 什么是泛型? 泛型是JDK 1.5的一項新特性,它的本質是參數化類型(Parameterized Type)的應用,也就是說所操作的數據類型被指定為一個參數,...
閱讀 3878·2021-09-27 13:36
閱讀 4554·2021-09-22 15:12
閱讀 3063·2021-09-13 10:29
閱讀 1826·2021-09-10 10:50
閱讀 2360·2021-09-03 10:43
閱讀 518·2019-08-29 17:10
閱讀 442·2019-08-26 13:52
閱讀 3249·2019-08-23 14:37