1. 程式人生 > >java泛型的理解,和為什麼擦出後,還可以得到

java泛型的理解,和為什麼擦出後,還可以得到

java泛型的理解,和為什麼擦出後,還可以得到

開篇

泛型的使用和例子不說了,太多同類型的文章,自己搜搜,本文主要說
  • 泛型的來源和影響
  • 泛型擦除
  • 泛型擦除了,為什麼反射時還可以得到

泛型的來源和影響

在1.5之前沒有泛型的只有class,所有的類都是class,也就是原始型別,我們統一定義了一個class類進行抽象,class類的一個具體物件就是一個類,後面有了泛型,超出了原始型別的定義,我們給class增加了4個平級的型別,他們是,1)引數化型別,就是用了泛型的類。2)型別變數型別,就是泛型裡面的那個型別變數。3)泛型表示式型別 4)引數化泛型陣列型別。這些都是和原始型別平齊的。
但是jvm只可以處理class原始型別,這個是java一開始就定義好的,如果要改的話,就是要在jvm中增加4中位元組碼檔案,對於jvm改的太大,所以我們不改,在javac編譯階段做相容。這個也就是為什麼我們說java的泛型是偽泛型,因為jvm並不支援泛型

泛型擦除

泛型擦除,就是在編譯階段,把我們寫的泛型轉化為jvm所支援的原始型別,也就是class型別,並且保持語法不變。這個中間過程很複雜。網上專門解釋的文章很多。記住就是jvm不認識我們書寫的泛型,javac要做一層轉化。這個也就是意味著我們在jvm中,也就是在執行中執行的程式碼,沒有泛型的程式碼。

泛型擦除了,為什麼反射時還可以得到

我們在上面說的泛型既然被擦除了,為什麼反射時還可以得到我們書寫的泛型呢,這個我找了很多資料才發現,為了方便反射操作,我們在java中定義了一個頂層介面 type他有

  • ParameterizedType: 表示一種引數化的型別,比如Collection
  • GenericArrayType: 表示一種元素型別是引數化型別或者型別變數的陣列型別
  • TypeVariable: 是各種型別變數的公共父介面
  • WildcardType: 代表一種萬用字元型別表示式,比如?, ? extends Number, ? superInteger【wildcard是一個單詞:就是“萬用字元”】

就是我們用class型別表示我們上面說的和原始型別平齊的4種類型,方便我們反射的時候使用,獲得泛型的資料。下面我們來回答上面的問題
最後我看資料發現原來javac在編譯的時候,確實在程式碼上把泛型都轉化為原始型別了,也就是實現了擦除,要不然也執行不了,但是對於類,方法和屬性的泛型(方法體內部除外)。javac編譯的時候專門在位元組碼中分配一個元資料存放 叫做Signature。存放我們泛型資訊,方便反射可以得到

import java.util.List;  
import java.util.Map;  
  
public class GenericClass<T> {                // 1  
    private List<T> list;                     // 2  
    private Map<String, T> map;               // 3  
      
    public <U> U genericMethod(Map<T, U> m) { // 4  
        return null;  
    }  
} 

//位元組碼
private java.util.Map map;  
  Signature: Ljava/util/Map;  
  Signature: length = 0x2  
   00 0A 
//位元組碼
const #10 = Asciz       Ljava/util/Map<Ljava/lang/String;TT;>;;  

我下面貼幾個比較好的,對我有幫助的連結

[新增連結描述](http://www.cnblogs.com/mylove7/articles/5811748.html)
[新增連結描述](http://rednaxelafx.iteye.com/blog/586212)