1. 程式人生 > >Type的子介面詳解(原始碼解析)

Type的子介面詳解(原始碼解析)

以下是原始碼中對Type的註釋:Type是Java中所有型別的常見的超介面,在程式語言中這些包括原始型別,引數化的型別,陣列型別,型別變數和原始型別。 Class在一定程度上挽救了擦除的型別資訊,我們就可以通過這幾個介面來獲取被擦除的型別引數資訊,這幾個介面無非就是對型別引數的一個分類,通過它們提供的一些方法,我們可以逐步的獲取到最原始的型別引數的Class物件。
Type的直接子介面 1.ParameterizedType: 表示一種引數化的型別,比如Collection,即普通的泛型。  形如:Object<T, K>,即常說的泛型
2.TypeVariable:是各種型別變數的公共父介面, 描述型別,表示泛指任意或相關一類型別,也可以說狹義上的泛型(泛指某一類型別),一般用K、V、E等。   3.GenericArrayType:表示一種元素型別是引數化型別或者型別變數的陣列型別,泛型陣列,描述的是形如:A<T>[]或T[]型別。List<>[],T[]這種。  4.WildcardType:代表一種萬用字元型別表示式,泛型表示式,但是在Java中並沒有WildcardType型別,也可以說是,限定性的泛型,形如:? extends classA、?super classB、? super T這樣的萬用字元表示式。

Type及其子介面的來歷 一. 泛型出現之前的型別: 沒有泛型的時候,只有所謂的原始型別。此時,所有的原始型別都通過位元組碼檔案類Class類進行抽象。Class類的一個具體物件就代表一個指定的原始型別。
二. 泛型出現之後的型別 :泛型出現之後,擴充了資料型別。從只有原始型別擴充了引數化型別、型別變數型別、泛型限定的的引數化型別 (含萬用字元+萬用字元限定表示式)、泛型陣列型別。
三. 與泛型有關的型別不能和原始型別統一到Class的原因 (1) 產生泛型擦除的原因
:本來新產生的型別+原始型別都應該統一成各自的位元組碼檔案型別物件。但是由於泛型不是最初Java中的成分。如果真的加入了泛型,涉及到JVM指令集的修改,這是非常致命的。 (2) Java中如何引入泛型 :為了使用泛型的優勢又不真正引入泛型,Java採用泛型擦除的機制來引入泛型。Java中的泛型僅僅是給編譯器javac使用的,確保資料的安全性和免去強制型別轉換的麻煩。但是,一旦編譯完成,所有的和泛型有關的型別全部擦除。 (3) Class不能表達與泛型有關的型別 :因此,與泛型有關的引數化型別、型別變數型別、泛型限定的的引數化型別 (含萬用字元+萬用字元限定表示式)、泛型陣列型別這些型別全部被打回原形,在位元組碼檔案中全部都是泛型被擦除後的原始型別,並不存在和自身型別一致的位元組碼檔案。所以和泛型相關的新擴充進來的型別不能被統一到Class類中。 (4) 與泛型有關的型別在Java中的表示 :為了通過反射操作這些型別以迎合實際開發的需要,Java就新增了ParameterizedType,GenericArrayType,TypeVariable 和WildcardType幾種型別來代表不能被歸一到Class類中的型別但是又和原始型別齊名的型別。 (5) Type的引入 :統一與泛型有關的型別和原始型別Class
引入Type的原因 為了程式的擴充套件性,最終引入了Type介面作為Class,ParameterizedType,GenericArrayType,TypeVariable和WildcardType這幾種型別的總的父介面。這樣實現了Type型別引數接受以上五種子類的實參或者返回值型別就是Type型別的引數。
Type介面中沒有方法的原因 從上面看到,Type的出現僅僅起到了通過多型來達到程式擴充套件性提高的作用,沒有其他的作用。因此Type介面的原始碼中沒有任何方法。

以下是 ParameterizedType 的原始碼: /** *ParameterizedType代表一個引數化的型別,比如<字串>集合。 * * 建立一個引數化型別第一次需要通過反射方法指定在這個包中。當建立引數化型別p,p例項化泛型型別宣告是解決,和所有型別引數p建立遞迴。看到{陣列。TypeVariable TypeVariable }詳情型別變數的建立過程。重複建立一個引數化的型別沒有影響。 * * 例項實現該介面的類必須實現一個equals()方法,把任意兩個例項共享相同的泛型型別宣告和型別引數平等。 **/ public interface ParameterizedType extends Type { /** *返回一個數組,該陣列表示該型別的實際型別引數。 *注意到在某些情況下,如果該型別表示巢狀在引數化型別中的非引數化型別,返回的陣列是空的。 *返回一個數組,該型別代表實際型別引數的物件 * 如果任何的實際型別引數指向一個不存在的型別宣告@丟擲 typenotpresentexception *如果任何實際的型別引數是一個引數化的型別不能被例項化的任何理由 @丟擲 malformedparameterizedtypeexception * @since 1.5 */ Type[] getActualTypeArguments(); //1.獲得實際型別
/** *返回表示該型別的類或介面的<型別>。 *返回表示該型別的類或介面的物件型別 * @since 1.5 */ Type getRawType(); //2.獲得<>前面實際型別
/** *返回一個表示該型別為成員的型別的物件型別。例如,如果這個型別是“O<T>.I<S>”,則返回一個“O<T>”的表示。 *如果該型別是頂級型別,則返回< null >。 *返回型別為該型別為成員的型別的物件。如果該型別是頂級型別,則返回 < null > * @丟擲typenotpresentexception 如果業主型指的是一個不存在的型別宣告 * @丟擲malformedparameterizedtypeexception 如果業主型指的是一個引數化的型別不能被例項化的任何理由 * @since 1.5 */ Type getOwnerType(); //3.如果這個型別是某個型別所屬,獲得這個所有者型別,否則返回null } 1.getActualTypeArguments   :獲得引數化型別中<>裡的型別引數的型別,因為可能有多個型別引數,例如Map<K, V>,所以返回的是一個Type[]陣列。【注意】無論<>中有幾層<>巢狀,這個方法僅僅脫去最外層的<>,之後剩下的內容就作為這個方法的返回值,所以其返回值型別不一定。 也就是說會獲得實際的引數型別的陣列。 2.getRawType   返回最外層<>前面那個型別,即Map<K ,V>的Map,即得到 宣告此引數化型別的類。 3.getOwnerType   :獲得這個型別的所有者的型別。 如果此型別為 O<T>.I<S>,則返回 O<T> 的表示形式。如果此型別為頂層型別,則返回 null。
{1}. 對於ArrayList>,通過getActualTypeArguments()返回之後,脫去最外層的<>之後,剩餘的型別是ArrayList。因此對這個引數的返回型別是ParameterizedType。 {2}. 對於ArrayList,通過getActualTypeArguments()返回之後,脫去最外層的<>之後,剩餘的型別是E。因此對這個引數的返回型別是TypeVariable。 {3}. 對於ArrayList,通過getActualTypeArguments()返回之後,脫去最外層的<>之後,剩餘的型別是String。因此對這個引數的返回型別是Class。 {4}. 對於ArrayListextends Number>,通過getActualTypeArguments()返回之後,脫去最外層的<>之後,剩餘的型別是? ExtendsNumber。因此對這個引數的返回型別是WildcardType。 {5}. 對於ArrayList,通過getActualTypeArguments()返回之後,脫去最外層的<>之後,剩餘的型別是E[]。因此對這個引數的返回型別是GenericArrayType。 所以,可能獲得各種各樣型別的實際引數,所以為了統一,採用直接父類陣列Type[]進行接收。
以下是 TypeVariable的原始碼: /** * TypeVariable是常見的超介面型別變數的型別。第一次建立一個型別變數需要通過反射方法,指定在這個包中。如果一個型別變數t是引用的型別T(i.e、類、介面或註釋型別),並且是T n封閉類的宣告,然後建立T需要解決 T的封閉類,因為我= 0到n、包容。建立型別變數不能導致其邊界的建立。重複建立型別變數沒有影響。 * * 在執行時可以例項化多個物件來表示給定的型別變數。即使只建立一次型別變數,但這並不意味著需要快取表示型別變數的例項。然而,所有的例項代表一個型別的變數必須equal()彼此。因此,型別變數的使用者不能依賴於實現此介面的類例項的標識。 * * @param < D >的泛型型別宣告宣佈底層型別變數。 * * @since 1.5 */
public interface TypeVariable<D extends GenericDeclaration> extends Type { /** * 返回一個數組型別的物件代表這類變數的上界。注意,如果沒有顯式宣告上限,上限是物件。 *對於每一個上界B:如果B是一個引數化的型別或型別變數,它是建立的,(為建立過程的細節參見引數化型別)。否則,B是已解決的。 * @throws TypeNotPresentException如果任何範圍指的是一個不存在的型別宣告   * @throws MalformedParameterizedTypeException如果任何範圍指的是引數化型別,不能因為任何原因被例項化   * @return陣列型別的代表上界(s)這種型別的變數 */ Type[] getBounds(); //獲得泛型的上限,若未明確宣告上邊界則預設為Object
/** *返回表示泛型宣告宣佈的這種GenericDeclaration物件變數。   * @return泛型宣告宣佈這種型別變數。 * @since 1.5 */ D getGenericDeclaration(); //獲取宣告該型別變數實體(即獲得類、方法或構造器名)
/** *返回這類變數的名稱,因為它發生在原始碼中。   * @return這種型別變數的名稱,因為它出現在原始碼 */ String getName(); //獲得名稱,即K、V、E之類名稱 } 大家可能會發現這個不僅繼承了Type還繼承了 GenericDeclaration介面該介面用來定義哪些物件上是可以宣告(定義)範型變數,所謂範型變數就是<E extends List>或者<E>, 也就是TypeVariable<D>這個介面的對應的物件,TypeVariable<D>中的D是extends GenericDeclaration的,用來通過範型變數反向獲取擁有這個變數的GenericDeclaration。 目前實現GenericDeclaration介面的類包括Class, Method, Constructor,也就是說只能在這幾種物件上進行範型變數的宣告(定義)。GenericDeclaration的介面方法getTypeParameters用來逐個獲取該GenericDeclaration的範型變數宣告。
//1.在類(Class)上宣告(定義)型別變數class A<T>{ T a;}A<String> as = new A<String>(); //之後這裡可用任意型別替換T,集合就是泛型的一個典型運用 //2.在方法上宣告(定義) public <E> void test(E e){} //方法上,型別變數宣告(定義)不是在引數裡邊,而且必須在返回值之前,static等修飾後
//3.宣告(定義)在構造器上 public <K> A(K k){}
注意:型別變數宣告(定義)的時候不能有下限(既不能有super),否則編譯報錯。因為T extends classA表示泛型有上限classA,當然可以,因為這樣,每一個傳進來的型別必定是classA(具有classA的一切屬性和方法),但若是T super classA,傳進來的型別不一定具有classA的屬性和方法,當然就不適用於泛型。
1.getBounds  :獲得該型別變數的上限(上邊界),若無顯式定義(extends),預設為Object,型別變數的上限可能不止一個,因為可以用&符號限定多個(這其中有且只能有一個為類或抽象類,且必須放在extends後的第一個,即若有多個上邊界,則第一個&後必為介面)。
2.getGenericDeclaration  :獲得宣告(定義)這個型別變數的型別及名稱,即如: class com.xxx.xxx.classA 或 public void com.fcc.test.Main.test(java.util.List) 或 public com.fcc.test.Main() 3.getName  :獲得這個型別變數在宣告(定義)時候的名稱

以下是GenericArrayType 的原始碼:
/** * GenericArrayType代表陣列型別的元件型別引數化的型別或型別變數。 * @since 1.5 */ public interface GenericArrayType extends Type { /** *返回一個表示元件型別的型別物件的陣列。這個方法建立陣列的元件型別。看到{陣列的宣告。ParameterizedType ParameterizedType }建立過程的引數化的語義型別,看看{陣列。TypeVariable TypeVariable }型別變數的建立過程。 * * @return表示元件型別的型別物件的陣列   * @throws TypeNotPresentException如果底層陣列型別的元件型別指的是一個不存在的型別宣告   * @throws MalformedParameterizedTypeException如果底層陣列型別的元件型別指的是一個引數化的型別,不能因為任何原因被例項化 */ Type getGenericComponentType(); //獲得這個陣列元素型別,即獲得:A<T>(A<T>[])或T(T[]) } GenericArrayType,泛型陣列,描述的是ParameterizedType型別以及TypeVariable型別陣列,即形如:classA<T>[][]、T[]等,是Type的子介面。 <>不能出現在陣列的初始化中,即new陣列之後不能出現<>,否則javac無法通過。但是作為引用變數或者方法的某個引數是完全可以的。獲取泛型陣列中元素的型別。 1. getGenericComponentType  獲取泛型陣列中元素的型別,要注意的是: 無論從左向右有幾個[]並列,這個方法僅僅脫去最右邊的[]之後剩下的內容就作為這個方法的返回值。
{1}. 對於String[],通過getComponentType()返回之後,脫去最右邊的[]之後,剩餘的型別是String。因此對這個引數的返回型別是Class {2}. 對於E[],通過getComponentType()返回之後,脫去最右邊的[]之後,剩餘的型別是E。因此對這個引數的返回型別是TypeVariable {3}. 對於ArrayList[],通過getComponentType()返回之後,脫去最右邊的[]之後,剩餘的型別是ArrayList。因此對這個引數的返回型別是ParameterizedType {4}. 對於E[][],通過getComponentType()返回之後,脫去最右邊的[]之後,剩餘的型別是E[]。因此對這個引數的返回型別是GenericArrayType




以下是WildcardType 的原始碼: /** * WildcardType代表一個萬用字元型別表示式,如?、?extends Number、?super Integer. * @since 1.5 */ public interface WildcardType extends Type { /** * 返回一個數組型別的物件代表上界(s)這種型別的變數。注意,如果沒有顯式宣告上限,上限是物件。 * * 對於每一個上界B:如果B是一個引數化的型別或型別變數,它是建立,為建立過程的細節引數化types。否則, B 是被解決的. * * @return陣列型別代表上界(s)這種型別的變數   * @throws TypeNotPresentException如果任何範圍指的是一個不存在的型別宣告   * @throws MalformedParameterizedTypeException如果任何範圍指的是引數化型別,不能因為任何原因被例項化 */ Type[] getUpperBounds(); //獲得泛型表示式上界(上限)
/** * 返回一個數組型別的物件代表下界(s)這種型別的變數。注意,如果沒有顯式宣告下限,下限是空的型別。在本例中,返回一個零長度陣列。 * * 為每一個下界B:如果B是一個引數化的型別或型別變數,建立它,(見{陣列。ParameterizedType ParameterizedType }為建立過程的細節引數化型別)。否則,B是被解決的。 * * @return陣列型別代表下界(s)這種型別的變數   * @throws TypeNotPresentException如果任何範圍指的是一個不存在的型別宣告   * @throws MalformedParameterizedTypeException如果任何範圍指的是引數化型別,不能因為任何原因被例項化 */ Type[] getLowerBounds(); //獲得泛型表示式下界(下限) // 一個或多個?語言規範;目前只有一個,但這個API允許泛化。 }
1.getUpperBounds  獲取泛型表示式上界,根據API的註釋提示:現階段萬用字元表示式僅僅接受一個上邊界或者下邊界,這個和定義型別變數時候可以指定多個上邊界是不一樣。但是API說了,為了保持擴充套件性,這裡返回值型別寫成了陣列形式。實際上現在返回的陣列的大小就是1,萬用字元?指定多個上邊界或者下邊界現在是會編譯出錯的(jdk1.7是這樣的,至於7及以後就不知道了)。  2.getLowerBounds  :獲取泛型表示式下界。
根據上面API的註釋提示:現階段萬用字元表示式僅僅接受一個上邊界或者下邊界,這個和定義型別變數時候可以指定多個上邊界是不一樣。但是API說了,為了保持擴充套件性,這裡返回值型別寫成了陣列形式。實際上現在返回的陣列的大小就是1,獲取萬用字元表示式物件的泛型限定的上邊界的型別,例如下面的方法: {1}. public static voidprintColl(ArrayListextends ArrayList> al){} 萬用字元表示式是:? extendsArrayList,這樣 extends後面是?的上邊界,這個上邊界是ParameterizedType型別。 {2}. public static  voidprintColl(ArrayListextends E> al){} 萬用字元表示式是:? extends E,這樣 extends後面是?的上邊界,這個上邊界是TypeVariable型別 {3}.public static  voidprintColl(ArrayListextends E[]> al){} 萬用字元表示式是:? extends E[],這樣 extends後面是?的上邊界,這個上邊界是GenericArrayType型別 {4}.public static  voidprintColl(ArrayListextends Number> al){} 萬用字元表示式是:? extends Number,這樣 extends後面是?的上邊界,這個上邊界是Class型別 最終統一成Type作為陣列的元素型別。