最課程師徒班的同學正在紅紅火火的學習中。彷彿、應該、必須,到了跟大家階段性彙報成果的時候了。為什麼,因為畢竟當時把師徒班說的那麼好,就業班的同學該要鬧革命了。
師徒班相較於就業班,雖然課程內容和總量完全一樣,但學員的學習過程掌握了更多的主動性。而檢驗主動性一個重要的標準,除了日常的作業和任務,就是學員們的部落格了。我們要求學員必須每週一篇博文。可以說,看學員部落格的過程,就可以知道大家的成長有多麼大了。
說的這麼好,來,給看看唄。
好滴。我們以後會陸續放出學員的優秀部落格。看部分同學的部落格,我都要怕怕了,怕很快我都要教不了他們了。長江後浪推前浪,一浪更比一浪浪~~~
那麼好,開篇是誰?開篇難道不應該選一位極具geek氣息的同學(diaosi)嗎?到底是geek還是diaosi,你們來評判:
哦,不不,帥氣就要露出來,怎能徒留背多芬:
我們劉同學的這篇部落格名為:《對Java萬用字元的個人理解(以集合為例)》。可以說,初學者能將泛型萬用字元及約定理解到這種程度是很不容易的。不信,你去看看身邊的開發者,關於這塊的概念,多數工作了多年的開發者可能都沒我們的小劉同學理解的深刻。並且,劉同學一開始就不滿足僅限於日常體系的學習,很早就去關注了一些優秀的程式設計資源,比如阿里的編碼規範(本文中也有體現的)。
咳咳,就業班的同學們,我馬上就要把師徒班的若干成功經驗應用到你們的頭上來了哦。當然,這也意味著你們將會犧牲更多的休息時間來完成作業和任務。不過,為了拿薪水更高的offer,一切都是值得的。
華麗的分割線,以下是劉同學部落格原文:
=====================================================================================================================================
對Java萬用字元的個人理解(以集合為例)
前言
最近在學習Java,當學到了泛型的萬用字元時,不是很理解PECS(Producer Extends Consumer Super)原則,以及<? extends E> 不能使用add方法和<? super E> 不能使用get方法(注意:僅能使用Object o = list.get(0);取得是Object物件),所以我對它進行了學習和理解,這篇博文用了簡單通俗的方法去講解add和get在萬用字元中的使用場景以及PECS原則。可能本人的水平有限,如果我的理解有誤或者內容錯誤,歡迎指出來,我好進行及時修改。
一、什麼是泛型萬用字元
<一>簡單定義泛型統配符
1. 在瞭解泛型的統配符之前,我們先了解下什麼是泛型,泛型是一種包含型別引數的類,值得注意的一點是這裡的型別必須是引用資料型別,而且放在尖括號< >內,這裡引進了型別引數,將類直接作為了引數。
2. 那麼是什麼泛型統配符呢,我沒有找到定義,所以我自己給它下了個定義。泛型萬用字元是在泛型的使用中,用來表示對泛型型別進行型別範圍限定的特殊符號。這裡用萬用字元就是為了表明要輸入的型別要在一定範圍之內,說的通俗一些其實就是一個型別取值範圍,而最大值是Object這是確定的。
<二>泛型萬用字元的分類
1. <?>:無限萬用字元,可以在?中放入裡放入任何引用資料型別,用來接收任意引用資料型別。
2. <? extends E>:這個表明的是萬用字元的上界,通俗講就如同取值範圍中的負無窮到E,即小於等於E的所有型別, 因為E是最大的型別(最大可以達到Object),表明可以輸入所有的E的子類和E,等下會進行細緻的講解。
3. <? super E>:這個表明的是萬用字元的下界,通俗講其取值範圍就是E到最大值Object(因為Object是所有類的基類),就是大於等於E,小於等於Object類。
注意:這裡能制定上界或者下界,但是不能同時制定,然後<? extends E>中的extends不一定表示類與類的繼承還可以表示實現的關係,然後萬用字元一般是用在方法的形參宣告和方法呼叫上,無法用於定義類和介面中。
二、泛型萬用字元講解
<一>萬用字元的使用以及程式碼演示
1.無限萬用字元<?>的使用:可以傳入任何引用資料型別
A 在呼叫方法時使用?萬用字元的過程中無法使用add方法。
原因分析:因為萬用字元?代表任意的資料型別,但是當我們呼叫的時候或者用在方法的宣告上,其實這個時候還是沒有給?萬用字元一個指定的引用資料型別,那麼Java出於安全起見,就不可能允許新增元素。
B 以上的add方法雖然無法呼叫,add<null>是例外。
原因分析:因為null可以給任意引用資料型別賦值,代表任意引用資料,但是很容易引起NullPointerException。
C 注意使用List<?>和List<Object>當作形參時的作用不能等同,比如當傳入List<Integer>時List<?>可以接收,但是List<Object>無法接收。
原因分析:因為?代表任何引數型別可以接收,但是List<Object>中雖然Object是所有子類的父類,但是List<Object>不是List<Integer>的父類,List<Object>是ArrayList<Object>等類的父類,這就是為什麼泛型前後要一致的原因,從陣列的角度去理解集合就比如Object[ ] arr不是Integer[ ] arr1的父類。
2.上界萬用字元<? extends E>的使用:可以傳入E和E的子類
A <? extends E>作為形參時例如List<? extends E>可以使用集合的get方法來獲取E或者E型別本身的元素。
原因分析:當我們用get方法時我們其實是在獲取集合裡內部的元素,但是我們的集合的資料型別還沒有確定,但是我們可以獲得一些明確的已知條件,那就是在<? extends E>中最大的型別是E,而且這個E最大是Object,所以我們可以利用這一點,那麼我們就可以清楚地瞭解到該集合裡面的獲取的元素肯定是E或者Object的子類,他們的範圍肯定小於E或者Object,那麼我們就可以用Object和E這兩個範圍比集合裡面的元素大的類去接收集合裡面的元素。(注:可能略顯囉嗦但是我就是想解釋清楚。)
B 在使用上界萬用字元時,無法呼叫add方法來新增非null的元素。
原因分析:由於上面已經說得很清楚了,<? extends E>作為形參時例如List<? extends E>這時最大型別是E和Object,但是我們不清楚最小的型別是什麼,因為此時?這個萬用字元沒有被賦值,我們呼叫add方法是要新增集合元素或者集合元素的子類,但是我們沒法明確肯定該集合元素型別,或者比該集合元素範圍更小的子類,那麼Java就不會允許新增元素。
3.下界萬用字元<? super E>的使用:可以傳入E或者E的父類
A 在使用下界萬用字元時,無法使用get方法獲取Object以外的元素,或者需要向下轉型,但是可能出現ClassCastException的異常。
原因分析:上界萬用字元,在使用get方法的時候,此時型別沒有明確還是問號?我們只能明確其最大父類或者介面時,我們才能接收,但是我們只能明白<? super E>作為形參時例如List<? super E>時,只能明確Object是最大父類,其他的一概不知,所以只能Object o = list.get(0)。
B 可以使用集合的add方法新增E或者E的子類。
原因分析:上界萬用字元已經解釋很清楚了,add方法新增元素時,?型別不確定就要明確該?型別的最小子類,只要比可能存在的最小子類或者子介面小的任意引用資料型別的物件,我們都可以將其新增,而下界萬用字元<? super E>當作形參時例如List<? super E>,此時E就是最小子類,此時add方法可以新增E或者E的父類或者介面。
<二>對PECS原則的解讀
1.什麼是PECS原則?
PECS是Producer Extends Consumer Super的遞迴縮寫,是Java中使用泛型萬用字元的原則。
2.阿里巴巴的萬用字元使用規約
泛型萬用字元<? extends T>來接收返回的資料,此寫法的泛型集合不能使用 add 方法,而 < ? super T> 不能使用 get 方法,做為介面呼叫賦值時易出錯。
說明:擴充套件說一下 PECS<Producer Extends Consumer Super> 原則:第一、頻繁往外讀取內容的,適合用<? extends E>。第二、經常往裡插入的,適合用<? super E> 。
3.對PECS原則的簡單解讀
字面意思是生產者要被繼承要被當作上界萬用字元<? extends E>的上界E,消費者要繼承其他類要被當成下界萬用字元<? super E>的下界E,再借助下阿里巴巴的泛型開發規約去理解下,應該就是當這個被傳入的型別需要進行很多get操作獲取資料的話,那麼請使用上界萬用字元這時這個上界就如同生產者一樣,因為它能被不斷get到,而當需要不斷進行add方法新增資料的話,請使用下界萬用字元這時這個下界就如同消費者一樣,因為它不斷地索取,因為我們在不斷地add元素給它。
再一次的分割線
===========================================================
1:劉同學的原文部落格在:http://www.cnblogs.com/JNovice/
2:師徒班持續招生中,想成為像劉同學一樣的優秀學員,請來這裡報名:http://www.zuikc.com