下面我總結(jié)了集合荧呐、泛型、數(shù)組轉(zhuǎn)集合等一些常見(jiàn)的陷進(jìn)纸镊,認(rèn)真看完倍阐,相信你絕對(duì)有所收獲。
1逗威、List 收捣,List<?> 與 List<Object> 有區(qū)別嗎?
說(shuō)實(shí)話庵楷,我敢保證很多人是不知道 List, List<?> 與 List<Object> 之間的區(qū)別的罢艾。
1楣颠、我們先來(lái)看看 List 與 List<Object>
很多可能覺(jué)得 List<Object>的用法與 List 是一樣的,例如很多人認(rèn)為
List<Object> list;
與
List list;
這兩種定義方法是一模一樣的咐蚯,然而他們是不一樣的童漩。看下面一段代碼
List t1 =newArrayList<>();// 編譯通過(guò)Listt2 = t1;//編譯失敗List t3 = t1;
t1 可以賦給 t2, 但是 t1 不能賦給 t3春锋,會(huì)拋出如下異常
從這里可以看出
List list;
與
List<Object> list;
是有區(qū)別的矫膨,List 變量可以接受任何泛型的變量,而 List 則不可以期奔。
進(jìn)群:697699179可以獲取Java各類(lèi)入門(mén)學(xué)習(xí)資料侧馅!
這是我的微信公眾號(hào)【編程study】各位大佬有空可以關(guān)注下,每天更新Java學(xué)習(xí)方法呐萌,感謝馁痴!
學(xué)習(xí)中遇到問(wèn)題有不明白的地方,推薦加小編Java學(xué)習(xí)群:697699179內(nèi)有視頻教程 肺孤,直播課程 罗晕,等學(xué)習(xí)資料,期待你的加入
2赠堵、我們?cè)诳纯?Lis<?> 有什么需要注意的地方:
看下面一段代碼:
List t1 =newArrayList<>();List t2 = t1;// 編譯通過(guò)t2.remove(0);? t2.clear();// 編譯不通過(guò)2.add(newObject());
List 是一個(gè)泛型小渊,在沒(méi)有賦值之前,是可以接受任何集合的賦值的茫叭,我想這點(diǎn)大家都知道酬屉,但是請(qǐng)注意,?賦值之后就不能往里面添加元素了?揍愁,提示如下錯(cuò)誤:
所以 List<?> 一般用來(lái)作為參數(shù)來(lái)接受外部的集合梆惯,或者返回一個(gè)不知道具體元素的集合。
List 與 List<?>, List<Object> 的細(xì)微區(qū)別知道了吧吗垮?
2垛吗、<? extends T> 與 <? super T>你真的懂嗎?
我們知道泛型 List<T> 只能放置一種類(lèi)型烁登,如果你采用 List<Object> 來(lái)放置多種類(lèi)型怯屉,然后再進(jìn)行類(lèi)型強(qiáng)制轉(zhuǎn)換的話,那會(huì)失去了泛型的初衷饵沧。
為了能夠放置多種類(lèi)型锨络,于是有了 與 ,下面先說(shuō)一些你?可能?原本就知道的知識(shí):
1狼牺、?對(duì)于 <? extends T> a?羡儿,a 這個(gè)變量可以接受 T 及其 T 子類(lèi)的集合,上界為 T是钥,并且從 a 取出來(lái)的類(lèi)型都會(huì)被強(qiáng)制轉(zhuǎn)換為 T掠归。重點(diǎn)看下面一個(gè)例子:
注意:我們先約定 Cat(貓) 繼承自 Animal(動(dòng)物)缅叠,RedCat(黑貓) 繼承自 Cat
List animals =newArrayList<>();List cats =newArrayList<>();List redCats =newArrayList<>();// 可以通過(guò)編譯List extendsCat = redCats;// 不能通過(guò)編譯,因?yàn)橹荒芙邮?Cat 及其子類(lèi)的集合extendsCat = animals;// 重點(diǎn)注意:下面三行都不能通過(guò)編譯extendsCat.add(newAnimal());? ? extendsCat.add(newCat());? ? extendsCat.add(newRedCat());// 重點(diǎn)注意:可以通過(guò)編譯extendsCat.add(null);
注意虏冻,最需要注意的是肤粱,就是?不能向里面添加除null之外的其他所有元素?,這個(gè)和 List 有點(diǎn)類(lèi)似厨相。
2领曼、現(xiàn)在說(shuō)說(shuō) ,它和 有點(diǎn)相反蛮穿。?對(duì)于 <? super T> a?庶骄,a 這個(gè)變量可以接受 T 及其 T 父類(lèi)的集合,下界為 T践磅,并且從 a 取出來(lái)的類(lèi)型都會(huì)被強(qiáng)制轉(zhuǎn)換為 Object单刁。重點(diǎn)看下面一個(gè)例子:
List animals =newArrayList<>();List cats =newArrayList<>();List redCats =newArrayList<>();// 可以通過(guò)編譯List superCat = animals;// 不能通過(guò)編譯,因?yàn)橹荒芙邮?Cat 及其父類(lèi)的集合superCat = redCats;// 重點(diǎn)注意:不能通過(guò)編譯,只能添加 Cat 及其 Cat 的子類(lèi)superCat.add(newAnimal());// 重點(diǎn)注意音诈,可以通過(guò)編譯superCat.add(newCat());? superCat.add(newRedCat());? superCat.add(null);
注意幻碱,<绎狭? super T>最需要注意的是细溅,在?雖然可以接受 T 及其父類(lèi)的賦值,但是只能向里面添加 T 及其 T 的子類(lèi)?儡嘶。
總結(jié)
1喇聊、List a ,可以把 a 及其 a 的子類(lèi)賦給 a,從 a 里取的元素都會(huì)被強(qiáng)制轉(zhuǎn)換為 T 類(lèi)型蹦狂,不過(guò)需要注意的是誓篱,?不能向 a 添加任何除 null 外是元素?。
2凯楔、List a ,可以把 a 及其 a 的父類(lèi)賦給 a窜骄,從 a 里取的元素都會(huì)被強(qiáng)制轉(zhuǎn)換為 Object 類(lèi)型,不過(guò)需要注意的是摆屯,可以向 a 添加元素邻遏,?但添加的只能是 T 及其子類(lèi)元素?。
3虐骑、泛型與重載
我們先來(lái)看一道題准验,你覺(jué)得下面這道題能夠?編譯?通過(guò)嗎?
publicclassGernerTypes{publicstaticvoidmethod(List<Integer> list){? ? ? ? System.out.println("List<Integer> list");? ? }publicstaticvoidmethod(List<String> list){? ? ? ? System.out.println("List<String> list");? ? }}
答是編譯不通過(guò)廷没。
兩個(gè)方法的參數(shù)不同糊饱,為什么會(huì)重載不通過(guò)呢?
實(shí)際上在 Java 的泛型中颠黎,泛型只存在于源碼中另锋,在編譯后的字節(jié)碼中滞项,泛型已經(jīng)被替換為?原生類(lèi)型?了,并且在相應(yīng)的地方插入了強(qiáng)制轉(zhuǎn)換的代碼砰蠢。為了方便理解蓖扑,可以看下面的一段代碼例子:
// 源碼publicstaticvoidmain(String[] args){? ? ? ? Listlist=newArrayList<>();list.add(1);? ? ? ? System.out.println(list.get(0));? ? }
編譯之后泛型就不存在了,并且在相應(yīng)的地方插入了強(qiáng)制轉(zhuǎn)換的代碼台舱,編譯之后律杠,我們反編譯的代碼如下:
// 反編譯之后的代碼publicstaticvoidmain(String[] args){? ? ? ? Listlist=newArrayList();list.add(1);? ? ? ? System.out.println((Integer)list.get(0));? ? }
這種 編譯之后泛型就不存在了,并且在相應(yīng)的地方插入了強(qiáng)制轉(zhuǎn)換代碼的機(jī)制我們也稱(chēng)之為?擦除?竞惋。
所以上面的兩個(gè)方法柜去,看似參數(shù)不一樣,但是經(jīng)過(guò)編譯擦出之后拆宛,他們的參數(shù)就是一樣的了嗓奢,所以編譯不通過(guò)。
4浑厚、數(shù)組與集合相互轉(zhuǎn)換時(shí)需要注意的點(diǎn)
1股耽、數(shù)組轉(zhuǎn)集合
大家先看一個(gè)例子吧,
publicstaticvoidmain(String[] args) {String[] arr = {"one","two","three"};// 數(shù)組轉(zhuǎn)換成集合List list = Arrays.asList(arr);// 向集合添加元素:編譯正常钳幅,但運(yùn)行時(shí)拋出了異常list.add("four");? ? }
向集合添加元素拋出了如下異常:
問(wèn)題來(lái)了物蝙,向集合添加元素為啥會(huì)拋出異常呢?敢艰?
我們先來(lái)看一下 Arrays.asList(arr) 方法究竟返回了什么诬乞?
源碼如下:
返回的明明是 ArrayList 啊,為啥就不能添加元素呢钠导?震嫉?
實(shí)際上,此 ArrayList 非彼 ArrayList牡属,這個(gè)返回的 ArrayList 實(shí)際上是 Arrays 的一個(gè)?內(nèi)部類(lèi)?票堵。該內(nèi)部類(lèi)也是十分簡(jiǎn)單,和真實(shí)的那個(gè) ArrayList 沒(méi)得比逮栅,部分源碼如下:
而且這個(gè)假的 ArrayList 是直接 引用原數(shù)組的悴势,不然你看它的構(gòu)造器(第二條畫(huà)線)
也就是說(shuō),ArrayList 內(nèi)部是直接引用 arr 數(shù)組证芭,你對(duì) arr 數(shù)組進(jìn)行改變瞳浦,也會(huì)同時(shí)改變到 list 集合。
下面的代碼證明這一點(diǎn)
publicstaticvoidmain(String[] args) {String[] arr = {"one","two","three"};// 數(shù)組轉(zhuǎn)換成集合List list = Arrays.asList(arr);// 修改 arrarr[0] ="0";//打印看看System.out.println(list.get(0));? ? }
打印結(jié)果是 “0”废士。
所以叫潦,我們向 list 添加元素肯定失敗,因?yàn)?arr 數(shù)組的長(zhǎng)度了 3 官硝,本來(lái)就有 3 個(gè)元素了矗蕊,你在向里面添加第四個(gè)元素短蜕,肯定是不行的。
所以傻咖,在把數(shù)組轉(zhuǎn)換為集合的過(guò)程中朋魔,需要特別注意。
建議大家這樣轉(zhuǎn)換比較安全
List list = new ArrayList<>(Arrays.asList(arr));
2卿操、集合轉(zhuǎn)數(shù)組
集合轉(zhuǎn)換為數(shù)組相對(duì)比較不苛刻警检,我就不拉很多源碼來(lái)進(jìn)行分析了,我只簡(jiǎn)單說(shuō)下幾個(gè)需要注意的地方害淤。例如對(duì)于下面這個(gè)轉(zhuǎn)換:
// 集合大小為 sizeList list =newArrayList<>();// 長(zhǎng)度為 n 的數(shù)組String[] arr =newString[n];// 進(jìn)行轉(zhuǎn)換list.toArray(arr);
1扇雕、如果數(shù)組長(zhǎng)度比集合小:由于 arr 的長(zhǎng)度不夠窥摄,所以集合里的元素不會(huì)賦給 arr镶奉,而且自己再重新創(chuàng)建一個(gè)新數(shù)組反回去。
2崭放、如果數(shù)組長(zhǎng)度不小于集合:此時(shí) arr 的長(zhǎng)度夠了哨苛,所以集合里的元素直接復(fù)制給 arr 數(shù)組,不會(huì)重新創(chuàng)建一個(gè)新的元素币砂。
一覽源碼:
public T[] toArray(T[] a) {if(a.length size)? ? ? ? a[size] =null;returna;? ? }
以上這些陷進(jìn)相信有不少人是不知道了建峭,我把它總結(jié)整理了出來(lái),如果大家看完覺(jué)得有收獲