Java泛型解析(03):虛擬機器執行泛型代碼,java03

來源:互聯網
上載者:User

Java泛型解析(03):虛擬機器執行泛型代碼,java03
Java泛型解析(03):虛擬機器執行泛型代碼
     Java虛擬機器是不存在泛型型別對象的,所有的對象都屬於普通類,甚至在泛型實現的早起版本中,可以將使用泛型的程式編譯為在1.0虛擬機器上能夠啟動並執行class檔案,這個向後相容性後期被拋棄了,所以後來如果用Sun公司的編譯器編譯的泛型代碼,是不能運行在Java5.0之前的虛擬機器的,這樣就導致了一些實際生產的問題,如一些遺留代碼如何跟新的系統進行銜接,要弄明白這個問題,需要先瞭解一下虛擬機器是怎麼執行泛型代碼的。       虛擬機器的一種機制:擦除型別參數,並將其替換成特定類型,沒有指定特定類型用Object代替,如前文中的Couple<T>類,虛擬機器擦除後:   [code01]

     public class Couple {           private Object wife ;           private Object husband ;           public Couple(Object  wife, Object  husband) {                    this.wife = wife;                    this.husband = husband;          }           public void setWife(Object  wife) {this. wife = wife;}           public void setHusband(Object  husband) {this. husband = husband;}                     public Object  getWife() {return wife;}           public Object  getHusband() {return husband;}     }
     型別參數T是一個任意類型的,所以擦除後用Object代替了。不管是Couple<Employee>或者Couple<String>擦除後都成為了原始類Couple類,這就好比回到了泛型引入Java之前的普通類。所以這裡重點圍繞著 擦除型別參數這個機制展開講解。     如有對型別參數有類型限定會怎麼替換呢?擦除型別參數機制告訴我們,使用限定的類型代替,如果有多個,使用第一個代替,看一段代碼: [code02]
     public class Period<T extends Comparable<T> & Serializable> {           private T begin;           private T end;           public Period(T one, T two) {                    if (one.compareTo(two) > 0) {begin = two;end = one;                   } else {begin = one;end = two;}          }     }
     code02擦除後,Period的原始類型如下: [code03]
     public class Period {           private Comparable begin;           private Comparable end;           public Period(Comparable one, Comparable two) {                    if (one.compareTo(two) > 0) {begin = two; end = one;                   } else {begin = one; end = two;}          }     }
     思考一下,如果將Period<T  extends Comparable<T> & Serializable>寫成Period<T  extends Serializable  & Comparable<T>>會是怎麼樣呢?同理,擦除後原始類型用第一個Serializable代替,這樣進行compareTo方法調用的時候,編譯器會進行必要的強制類型轉換,所以為了提高效率,將標籤介面(沒有任何方法的介面,也叫tagging介面)放在後面。     先來看看虛擬機器執行運算式的時候發生了什麼,如:[code04]
     Couple<Employee> couple = ...;     Employee wife = couple.getWife();
     擦除後,getWife()返回的是Object類型,然後虛擬機器會插入強制類型轉換,將Object轉換為Employee,所以虛擬機器實際上執行了兩天指令:     1.調用Couple.getWife()方法。     2.將Object轉換成Employee類型。      再來看看虛擬機器執行泛型方法的時候發生了什麼,泛型方法如: [code05]
public static <T extends Comparable<T>> max(T[] arrays) {... }擦除後成了:public staticComoparable max(Comparable[] arrays) {... }
     但是泛型方法的擦除會帶來兩個複雜的問題,且看第一個執行個體,一個執行個體:[code06]
     public class Period <T extends Comparable<T> & Serializable> {           private T begin;           private T end;           public Period(T one, T two) {                    if (one.compareTo(two) > 0) {begin = two;end = one;                   } else {begin = one;end = two;}          }          public void setBegin(T begin) {this. begin = begin;}          public void setEnd(T end) {this. end = end;}          public T getBegin() {return begin;}          public T getEnd() {return end;}     }     public class DateInterval extends Period<Date> {           public DateInterval(Date one, Date two) {                    super(one, two);          }           public void setBegin(Date begin) {                    super.setBegin(begin);          }     }
     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<Date> period  = new DateInterval(...);     period.setBegin(new Date());
     這裡因為period引用指向了DateInterval執行個體,根據多態性,setBegin應該調用DateInterval對象的setBegin方法,可是這個擦除讓Period中的  public  void setBegin(Object begin) {...}被調用,導致了擦除與多態發生了衝突,怎麼辦呢?虛擬機器此時會在DateInterval類中產生一個橋方法(bridge method),調用過程發生了細微的變化: [code08]
     public void setBegin(Object begin) {          setBegin((Date)begin);      }
     有了這個合成的橋方法以後,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     public Date getBegin(){ return super.getBegin(); }
     這裡用了@Override,說明是覆蓋了父類的Object getBegin()方法,而返回值可以指定為父類中的返回值類型的子類,這就是協變類型,這是Java5以後才可以允許的,允許子類覆蓋了方法後指定一個更嚴格的類型(子類型)。 總結:     1.記住一點,虛擬機器中沒有泛型,只有普通的類。     2.所有泛型的型別參數都用它們限定的類型代替,沒有限定則用Object。     3.為了保持型別安全,虛擬機器在有必要時插入強制類型轉換。     4.橋方法的合成用來保持多態性。     5.協變類型允許子類覆蓋方法後返回一個更嚴格的類型。

Java泛型解析(01):認識泛型
Java泛型解析(02):萬用字元限定
Java泛型解析(03):虛擬機器執行泛型代碼

java泛型完成數組問題,剛學泛型,協助

整數數組的順序收縮

public static int seqSearch(int[] arr, int first, int last, int target) {
for (int i = first; i < last; i++)
if (arr[i] == target)
return i;

return -1;
}
 
JAVA中list的泛型問題

是的

上面是ArrayList的get方法,返回時是強轉了
如果不指定泛型就是Object類型的,如果指定了泛型,類型不對的話你put都put不進去的


 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.