昨天有同事問 UserService、XxxService 都會調用 Dao 的 insert、update ... ...,這些重覆的代碼,有沒有辦法變得靈活一些? 巧了,和咱們分享的主題剛好碰上,賣個關子,先不談解決方案,就當啥事沒有發生,重新引入今天的話題(捂嘴笑)。 想蛻變的研發人員,偶爾會 ...
昨天有同事問 UserService、XxxService 都會調用 Dao 的 insert、update ... ...,這些重覆的代碼,有沒有辦法變得靈活一些?
巧了,和咱們分享的主題剛好碰上,賣個關子,先不談解決方案,就當啥事沒有發生,重新引入今天的話題(捂嘴笑)。
想蛻變的研發人員,偶爾會品味一下 Java 的源碼;久經職場的碼農,時不時也會搭建一下項目架構。其實無論你是剛入猿門,還是骨灰級戰神,今天的分享你多多少少都聽過、關註過、迷茫過甚至用過。
好了,準備好小板凳,讓我們一起聊聊,在你看源碼、搭架構過程中都躲避不開的 Java 中那些 E、T、?等字母都是啥意思?
先科普一下知識,什麼是泛型?聊啥概念,直接上代碼,直奔主題,先從 JDK 1.8 摘點源碼出來,一起與泛型打個照面,混個臉熟。
1.
啥是 E?
E 可以說在 JDK 源碼中無處不在,咱們就從 ArrayList 的源碼進行聊起。
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { /** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return <tt>true</tt> (as specified by {@link Collection#add}) */ public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } // 更多代碼 ... ... }
這個 E 在此處用來創建與初始 ArrayList 的類型,其實我們可以把它換成實際的類型,舉個慄子:
發現編譯器會把 E 被真正的類型所取代,其實也就是當我們創建出 ArrayList<String>,那麼對應的 add() 會變成 add(String e);當我們創建出 ArrayList<Dog>,那麼對應的 add() 會變成 add(Dog e)。
但是往往一不留神你會這麼寫,其實你也不想這麼寫,程式猿心裡的苦,其實說也說不清楚(捂嘴笑)。
這就說明瞭為什麼寫代碼的時候,老是經常編譯不過去,老是有警告,誰讓咱定義的是 String 類型,而咱們又非要往集合中放入一條狗呢。
來自靈魂的拷問:只能用“E”來表示嗎?(身邊同事還真問過我這個問題,在這我還是再認真的回答一次)
其實我們可以使用任何合法的 Java 標識字元串,但是大家用單一的字母來表示,已經成為一種習慣,而 E 又代表 Element 元素的意思,所以在集合中經常用 E 來表示,但是源碼中大概率會碰到字母 T。
2.
啥是T?
public static <T extends Comparable<? super T>> void sort(List<T> list) { list.sort(null); }
上面代碼摘自 JDK 1.8 Collections 的源碼,我們發現了“T”,而且還有“?”,問號咱們暫且放一邊,著重聊一聊“T”。
分兩部分去看,其中第一部分 <T extends Comparable> 指的是 T 必須是 Comparable 的實現(這是重點、這是重點、這是重點);第二部分方法入參 sort(List<T> list),指的是僅能傳入繼承 Comparable 的參數化類型的 list。
其實也很難理解,不妨再摘個慄子配合理解。
從 JDK 1.8 源碼中摘出 String 的源碼,把 String 代入 Collections 的 sort 方法,替換為 T 嘗試悟一下,看看是否 ok?!變數替換數學中大家都學過,就不深入了。
但是你實際開發中,有沒有遇到過上圖的情形,在進行狗狗列表排序時,就死活報錯!報錯!!原因就是因為要排序的狗狗,必須要實現 Comparable,方能進行排序。
好了,T 就不再聊了,咱們還是說說這個問號吧。
3.
“?”問號是啥?
問號,看到這個估計會一臉懵逼,其實就是未知,代表一萬種可能性,在 Java 中就是萬用字元。
那我們再看看上面摘自 JDK 1.8 Collections 的源碼,那麼 Comparable<? super T> 則代表 Comparable 的類型參數必須是 T 或 T 的父型,你可能有迷糊了,還是再拋點代碼吧。
看到效果了沒,因為要針對狗狗排序,排序的類型必須是 Dog 或者是 Dog 的父類型,咱們傳入 String 類型,當然是編譯不通過啦,不妨改成 Dog 或者 Object 自己試一下,看看效果,在此不做演示。不過結論還是要說一下:Comparable<? super T> 則代表 Comparable 的類型參數必須是 T 或 T 的父類型。
提到 <? super T>,那不得不再提一提 <? extends T>,其實只要上面的搞明白了,這個也就非常清晰了,問號代表繼承和實現 T,慄子就不拋了,在框架源碼中遇到知道是啥意思就行了。
4.
任性的總結。
其實泛型是編譯期的一種檢查,能夠有效防止狗入人海,其中主要分為使用泛型的類以及使用泛型的方法;其中 E 主要用於集合的元素,除了 E 之外絕大部分是 T,然後 Java 還引入了一種萬用字元是問號,不過可以用任意 Java 有效標識符進行表示,不要再糾結、不要再糾結、不要再糾結。
說了這麼多,咱們開篇的問題還沒有解決啊?話不多說,直接拋代碼,不懂也沒關係,註意理解上面幾個字母就行了,下麵這段代碼分享給需要的朋友(哎呦我去,又出來個字母 D)。
@Transactional(readOnly = true) public abstract class CrudService<D extends CrudDao<T>, T extends DataEntity<T>> extends BaseService { /** * 持久層對象 */ @Autowired protected D dao; /** * 獲取單條數據 * @param entity * @return */ public T get(T entity) { return dao.get(entity); } /** * 查詢列表數據 * @param entity * @return */ public List<T> findList(T entity) { return dao.findList(entity); } /** * 查詢分頁數據 * @param page 分頁對象 * @param entity * @return */ public Page<T> findPage(Page<T> page, T entity) { entity.setPage(page); page.setList(dao.findList(entity)); return page; } /** * 保存數據(插入或更新) * @param entity */ @Transactional(readOnly = false) public int save(T entity) { if (entity.getIsNewRecord()){ entity.preInsert(); return dao.insert(entity); }else{ entity.preUpdate(); return dao.update(entity); } } /** * 刪除數據 * @param entity */ @Transactional(readOnly = false) public int delete(T entity) { return dao.delete(entity); } }
5.
好了,今天分享就到這兒吧,希望能夠解你所惑;希望能在你前進的道路上,幫你披荊斬棘。如果感覺有點幫助,歡迎在看、秒贊,瘋狂分享轉發,因為你的每一次分享,我都認真當成了鼓勵與喜歡。