總結筆記(一) - Java 泛型總結

泛型總結

泛型是什麼?

一句話說就是類型參數化。什麼意思呢?參數化的意思就是咱們在定義的時候不知道具體的值,咱們在到咱們實際運行的時候才知道具體的值。類型參數化就是具體類型在定義的時候不知道,在實際運行的時候是肯定的某一個類型。url

Java 是如何實現泛型的?

泛型是不少高級語言都有的特性。根據定義,泛型在運行時表示同一個類型,咱們比較容易想到 List<A>List<B> 用 2 個不一樣的 Class 表示,這個是可行的,可是 Java 因爲須要兼容支持舊的代碼,並且在推出泛型前就提供了容器類,這種方式(List<A>List<B> 用 2 個不一樣的 Class)沒法兼容之前的老代碼,因此這個實現方法不適用。因此 Java 大佬們想了另一種方式來實現泛型,這種方式就是類型擦除.net

什麼是泛型的類型擦除呢?

類型擦除就是在實際生成字節碼的時候,編譯器源碼裏面定義的 List<A> 變成了 List<Object>,源碼裏面定義的 A Class擦除了,變成了 Object,同時在使用的時候,會強制類型轉換,把取出來的 object 轉成 A 的實例去使用。這就是類型擦除。code

初步看,泛型擦除好像是沒什麼大的問題,可是仔細想一想,在強制類型轉換的時候,因爲會丟掉類型的一些信息,會致使一些不符合預期的事情。好比有個基類 A,和它的兩個子類 B 和 C ,而後咱們有下面的一段代碼。get

List<A> listA = new ArrayList<A>();
listA.add(new B()); // 錯誤的,

第二行代碼是不符合預期的,由於 listA 裏面指望放的是 A 而不是 B。 可是這個好像不太符合預期,咱們有時候但願子類是能夠放進容器裏面的。可是若是支持這個操做的話,會發生什麼呢?取出來來的是 B 仍是 C ?若是不能明確,那麼就沒有實現「泛型」。編譯器

爲了解決這個問題, Java 大佬們想了個方法,提出了一些通配符來解決這些問題。源碼

泛型的通配符 ?extendssuper

在理解通配符以前,咱們須要知道的是,通配符的發明是爲了解決什麼問題?至少要解決的一個問題是:容器裏面放進去的是什麼,取出來的就是什麼。io

這個問題,其實分兩步,放進去,是說放進去同一種類型的東西。取出來,是說取出同一種類型的東西。或者說,用到通配符的地方應該是在不一樣的地方,一個地方把數據寫到容器,另一個地方把數據從容器拿出來,若是實在同一個代碼塊裏寫入和讀取數據到同一個容器,應該是知道具體類型的,是不須要用到通配符的。編譯

? 通配符

? 通配符稱爲無限通配符,表示不肯定或者不關心類型。class

extends 通配符

通常稱爲上界通配符,表示的意思是:取值範圍爲 (某個類的子類, 某個類]。再想一想咱們以前說的,通配符要解決的問題?放進去的是什麼,取出來的就應該是什麼。放數據和取數據應用在不一樣的場景。若是咱們在同一個場景,就不須要用到通配符了,由於類型是已知的。容器

經過上面的表述,容易推斷出來 <? extends E> 的集合只能往外拿數據,由於取出來的必定是 E ,可是放進去的不知道是什麼,多是 E ,也多是 E 的子類,若是容許往集合裏面放東西,就不能保證放進去的是什麼,拿出來的就是什麼了。由於只能保證拿出來的是 E

這個特性也叫作協變。

super 通配符

通常稱爲下界通配符,表示的意思是:取值範圍爲 [某個類,這個類的父類)。結合上面小節的解釋,能夠推斷出 <? super S> 的集合只能往裏面放數據,而不能從裏面拿東西,爲何呢?由於 <? extends E> 解決的就是拿出來的問題啊,因此這個解決的就是放進去的問題啊,囧。裏面放的是下限或者下限的子類。

這個特性也叫作逆變。

小結

通配符與一個規則, PE-CS

  • PE 簡單的說,當只想從集合中獲取元素,請把這個集合當作生產者,請使用<? extends T>,這就是 Producer extends 原則,PECS原則中的PE部分。集合生產元素後,就能夠拿過來用了。

  • CS 簡單的說,當你僅僅想增長元素到集合,把這個集合當作消費者,請使用<? super T>。這就是 Consumer super 原則,PECS原則中的CS部分。集合消費元素,這樣就能夠往裏面放了

  • 同時做爲生產者和消費者的狀況不存在,由於你能夠指定具體的泛型。

參考資料:

相關文章
相關標籤/搜索