天天看點

Java泛型解析(03):虛拟機運作泛型代碼

java泛型解析(03):虛拟機運作泛型代碼

     java虛拟機是不存在泛型類型對象的,全部的對象都屬于普通類,甚至在泛型實作的早起版本号中,可以将使用泛型的程式編譯為在1.0虛拟機上可以執行的class檔案,這個向後相容性後期被抛棄了,是以後來假設用sun公司的編譯器編譯的泛型代碼,是不能執行在java5.0之前的虛拟機的,這樣就導緻了一些實際生産的問題,如一些遺留代碼怎樣跟新的系統進行銜接,要弄明确這個問題,須要先了解一下虛拟機是怎麼執行泛型代碼的。

       虛拟機的一種機制:擦除類型參數,并将其替換成特定類型,沒有指定特定類型用object取代,如前文中的couple<t>類,虛拟機擦除後:   

[code01]

     類型參數t是一個随意類型的,是以擦除後用object取代了。無論是couple<employee>或者couple<string>擦除後都成為了原始類couple類,這就好比回到了泛型引入java之前的普通類。是以這裡重點環繞着擦除類型參數這個機制展開解說。

     如有對類型參數有類型限定會怎麼替換呢?擦除類型參數機制告訴我們,使用限定的類型取代,假設有多個,使用第一個取代,看一段代碼:

[code02]

     code02擦除後,period的原始類型例如以下:

[code03]

     思考一下,假設将period<t extends comparable<t> & serializable>寫成period<t extends serializable  & comparable<t>>會是怎麼樣呢?同理,擦除後原始類型用第一個serializable取代,這樣進行compareto方法調用的時候,編譯器會進行必要的強制類型轉換,是以為了提高效率,将标簽接口(沒有不論什麼方法的接口,也叫tagging接口)放在後面。

     先來看看虛拟機運作表達式的時候發生了什麼,如:

[code04]

     擦除後,getwife()傳回的是object類型,然後虛拟機會插入強制類型轉換,将object轉換為employee,是以虛拟機實際上運作了兩天指令:

     1.調用couple.getwife()方法。

     2.将object轉換成employee類型。

     再來看看虛拟機運作泛型方法的時候發生了什麼,泛型方法如:

[code05]

     可是泛型方法的擦除會帶來兩個複雜的問題,且看第一個執行個體,一個執行個體:

[code06]

     dateinterval類型擦除後,period中的方法變成:

     public void setbegin(object begin) {...}

     而dateinterval中的方法還是:

     public void setbegin(date begin) {...}

     是以dateinterval從period中繼承了 public void setbegin(object begin) {...}而自身又存在

public void setbegin(date begin) {...}方法,使用者使用時問題發生了:

[code07]

     這裡由于period引用指向了dateinterval執行個體,依據多态性,setbegin應該調用dateinterval對象的setbegin方法,但是這個擦除讓period中的 public void setbegin(object begin) {...}被調用,導緻了擦除與多态發生了沖突,怎麼辦呢?虛拟機此時會在dateinterval類中生成一個橋方法(bridge method),調用過程發生了細微的變化:

[code08]

     有了這個合成的橋方法以後,code07中對setbegin的調用過程例如以下:

      1.調用dateinterval.setbegin(object)方法。

      2.dateinterval.setbegin(object)方法調用dateinterval.setbegin(date)方法。

     發現了嗎,當我們在dateinterval中添加了getbegin方法之後會是什麼樣子的呢?是不是peroid中有一個object getbegin()的方法,而dateinterval中有一個date getbegin()方法呢,這兩個方法在java中是不能同一時候存在的?但是java5以後添加了一個協變類型,使得這裡是被同意的,看看dateinterval中getbegin方法就知道了:

[code09]

     這裡用了@override,說明是覆寫了父類的object getbegin()方法,而傳回值能夠指定為父類中的傳回值類型的子類,這就是協變類型,這是java5以後才幹夠同意的,同意子類覆寫了方法後指定一個更嚴格的類型(子類型)。

總結:

     1.記住一點,虛拟機中沒有泛型,僅僅有普通的類。

     2.全部泛型的類型參數都用它們限定的類型取代,沒有限定則用object。

     3.為了保持類型安全性,虛拟機在有必要時插入強制類型轉換。

     4.橋方法的合成用來保持多态性。

     5.協變類型同意子類覆寫方法後傳回一個更嚴格的類型。