1. 程式人生 > >封裝設定屬性,一家人都要整整齊齊系列(1) JAVA泛型的實現原理

封裝設定屬性,一家人都要整整齊齊系列(1) JAVA泛型的實現原理

1.基本學過JAVA的人都知道一點泛型,明白常出現的位置和大概怎麼使用。在類上為:class 類名<T> {}  在方法上為:public <T> void 方法名 (T x){}就不再贅述了。

  2.泛型就是將型別變成了引數去傳入,使得可以使用的型別多樣化,進而實現解耦。JAVA因為泛型是在1.5以後出現的,為了保持對以前版本的相容,使用了擦除的方法實現泛型。所以比起C++等在使用上限制較多。擦除是什麼呢,實際上就是一定程度無視了型別引數T,直接從T所在的類開始向上T的父類去擦除(或者說轉型?)比如:我呼叫泛型方法,傳了型別引數T進入方法內部,如果沒在宣告時做類似public T 方法名(T extends 某個父類   t){}這樣的宣告,在我的參

數T和t傳進去後,JAVA就進行了向上型別的擦除,直接把引數t當做Object類來處理,而不是我傳進去的T。如果我聲明瞭向上的界 限T extends Father,那就會把引數t擦除到Father類,當做Father類來使用。 也就是說,在有泛型的任何類或方法的內部,它都無法知道自己的泛型引數,就像泛型引數沒有傳入類或方法裡面一樣。那為何泛型引數會在類和方法上起作用? 根據《Thinking in JAVA》裡描述,擦除和轉型都是在邊界上發生的,也就是說我們傳進去的引數在進入類或方法的時候被擦除掉了, 但是在傳出來的時候又被轉型成了我們設定的T。 根據這一情況我們可知道,在泛型類或方法內,任何涉及到具體型別(即擦除後的型別的子類)的操作都不能進行
比如: new T();或者T.play()(play為某子類的方法而不是擦除後的類的方法),又或者同時有T.method(T t),Tmethod(K t)的方法

  3.在泛型裡如下的例子無法成立 List<Number> list = new List<Integer>();因為在編譯器看來List<Number>不是List<Integer>的父類(我也不知道具體原因,誰知道告訴我下),雖然在我們看來Number是 Integer的父類,那麼他們組成的容器List應該有這種繼承關係。為了實現這種繼承關係,我們可以使用萬用字元?。這樣寫就可以了

List<? extends Number>,這個是聲明瞭這個List的內部成員繼承自 Number,確定了List的上界。那麼我們就能執行List<? extends Number> list = new List<Integer>();了。但是要注意,原本我們泛型是引數型別T,規定了型別(你輸入的啥型別就規定的啥),所以我們在執行list.add()的時候只能輸入型別T的資料。在使用統配符後我們擴大了可選型別的範圍,所以我們的操作也受到了限定,比如:list.add(),因為我們可以選的實 例化List太多了,只要是Number的子類就行,JAVA可不知道我們會例項化什麼List,為了確保安全就統統不能add了。但是我們可以 list.get(),JAVA會把我們get的全都變成Number型別輸出(里氏替換原則)。有了上界就能有下界,List<? super Integer>,和上界相反。因為我們不知道我們會是哪個Integer的父類,我們無法get到任何東西,但是我們可以add進去,只要add的是Integer和它的子類就行(還是里氏原則)。 而且我們能使用List<? extends T>這種做法來進一步增加我們的選擇。

 4.還有無界萬用字元<?>,根據以上推論就知道其作用和限制了,它與不加泛型的List相比在於強調它是規定了某類的List,而不是Object類的List,只是我也不確定是什麼類。

以上部分轉載自https://www.cnblogs.com/taojinxuan/p/7222287.html

以下是我的個人見解:首先解決作者提出的問題:為什麼List<Number>不是List<Integer>的父類?

答:因為Java用的是單繼承模型,他已經有一個爸爸了


然後,總結一下,這篇部落格並沒有很好的排版,所以閱讀比較麻煩,為了讓作者的意圖更加明確地表達出來,我大概做一下整理。

我們可以通過<T extends ...>和<T super ...>的方式來設定上下限,也就是在編譯的時候根據“里氏代換原則”進行設定預設的型別。如果沒有設定上下限,就預設為Object。所謂的“擦除和轉型都是在邊界上發生的”就是這個意思。

然後作者說了get和add方法不能使用的問題,道理其實也是一樣的,一切建立在“里氏代換原則”上,在設定泛型方法時,會設定一個預設值,也就是所謂的“邊界”。add被禁止的情況下,因為子類有可能重寫父類的某些方法(個人理解),所以如果要執行對應的T.method()時,就會出現使用的方法不對的問題,因為不能保證所有的方法在所有子類中的實現時相同的。

接著是get被禁用的情況,因為只設置了下限,也就是子類,這樣如果在List中添加了父類,但是呼叫了一個子類所獨有的方法,但是父類並沒有這個方法,情況就會變得很尷尬。

基本就是這樣,後面我將會繼續探討當泛型和lambda表示式結合的非常有趣的情況,從今天開始,我要好好寫部落格了==!