標籤:就會 下界 oid 應該 編譯 object add 泛型 java泛型
1.基本學過JAVA的人都知道一點泛型,明白常出現的位置和大概怎麼使用。
在類上為:class 類名<T> {} 在方法上為:public <T> void 方法名 (T x){}
就不再贅述了。
2.泛型就是將類型變成了參數去傳入,使得可以使用的類型多樣化,進而實現解耦。
JAVA因為泛型是在1.5以後出現的,為了保持對以前版本的相容,使用了擦除的方法實現泛型。所以比起C++等在使用上限制較多。
擦除是什麼呢,實際上就是一定程度無視了型別參數T,直接從T所在的類開始向上T的父類去擦除(或者說轉型?)比如:我調用
泛型方法,傳了型別參數T進入方法內部,如果沒在聲明時做類似public T 方法名(T extends 某個父類 x){}這樣的聲明,在我的參
數T和x傳進去後,JAVA就進行了向上類型的擦除,直接把參數x當做Object類來處理,而不是我傳進去的T。如果我聲明了向上的界
限T extends Father,那就會把參數x擦除到Father類,當做Father類來使用。
也就是說,在有泛型的任何類或方法的內部,它都無法知道自己的泛型參數,就像泛型參數沒有傳入類或方法裡面一樣。
那為何泛型參數會在類和方法上起作用?
根據《Thinking in JAVA》裡描述,擦除和轉型都是在邊界上發生的,也就是說我們傳進去的參數在進入類或方法的時候被擦除掉了,
但是在傳出來的時候又被轉型成了我們設定的T。
根據這一情況我們可知道,在泛型類或方法內,任何涉及到具體類型(即擦除後的類型的子類)的操作都不能進行。比如:
new T();或者T.play()(play為某子類的方法而不是擦除後的類的方法),又或者同時有T.method(T x),Tmethod(K x)
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,只是我也不確定是什麼類。
JAVA泛型的實現原理