1. 程式人生 > 程式設計 >Java泛型的用法及T.class的獲取過程解析

Java泛型的用法及T.class的獲取過程解析

這篇文章主要介紹了Java泛型的用法及T.class的獲取過程解析,文中通過示例程式碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

胡亂總結泛型的四點作用:

第一是泛化,可以拿個T代表任意型別。 但GP是被C++嚴苛的靜態性逼出來的,落到Java、C#這樣的花語平原裡----所有物件除幾個原始型別外都派生於Object,再加上Java的反射功能,Java的Collection庫沒有範型一樣過得好好的。

第二是泛型 + 反射,原本因為Java的泛型拿不到T.class而覺得泛型沒用,最近才剛剛學到通過反射的API來獲取T的Class,後述。

第三是收斂,就是增加了型別安全,減少了強制型別轉換的程式碼。這點倒是Java Collection歷來的弱項。

第四是可以在編譯期搞很多東西,比如MetaProgramming。但除非能完全封閉於框架內部,框架的使用者和擴充套件者都不用學習這些東西的用法,否則那就是自絕於人民的票房毒藥。C++的MetaProgramming好厲害吧,但對比一下Python拿Meta Programming生造一個Class出來的簡便語法,就明白什麼才是真正的叫好又叫座。

所以,作為一個架構設計師,應該使用上述的第2,3項用法,在框架類裡配合使用反射和泛型,使得框架的能力更強; 同時採用收斂特性,本著對人民負責的精神,用泛型使框架更加型別安全,更少強制型別轉換。

擦拭法避免了Java的流血分裂 :

大家經常罵Java GP的擦拭法實現,但我覺得多虧於它的中庸特性---如果你用就是範型,不用就是普通Object,避免了Java陣營又要經歷一場to be or not to be的分裂。

最大的例子莫過Java 5的Collection 框架, 比如有些同學堅持認為自己不會白痴到型別出錯,而且難以忍受每個定義的地方都要帶一個泛型定義List〈Book〉,不用強制型別轉換所省下的程式碼還不夠N處定義花的(對了,java裡面還沒有tyepdef.....),因此對範型十分不感冒,這時就要齊齊感謝這個搽拭法讓你依然可以對一個泛型框架保持非泛型的用法了...

<<乾貨來了!!!!!!>>

通過反射獲得 T.class:

abstract public class BaseHibernateEntityDao<T> extends HibernateDaoSupport {
    private Class<T> entityClass;
    public BaseHibernateEntityDao() {
      entityClass =(Class<T>) ((ParameterizedType) getClass()
          .getGenericSuperclass()).getActualTypeArguments()[0];
} public T get(Serializable id) { T o = (T) getHibernateTemplate().get(entityClass,id); return o; } }

重點就是這句話:

Class<T> entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; 

解釋:

1. public Type getGenericSuperclass()

用來返回表示當前Class 所表示的實體(類、介面、基本型別或 void)的直接超類的Type。如果這個直接超類是引數化型別的,則返回的Type物件必須明確反映在原始碼中宣告時使用的型別。比如:

import java.lang.reflect.ParameterizedType; 
public class GT1 extends GT2<Integer>{ 
  public static void main(String[] args) { 
    System.out.println(((ParameterizedType)new GT1().getClass().getGenericSuperclass())); 
  } 
}

則輸出結果即為:

GT2<java.lang.Integer>

如果此Class代表的是Object 類、介面、基本型別或 void,則返回 null。。如果此物件表示一個數組類,則返回表示 Object 類的 Class 物件。

2. public Type[] getGenericInterfaces()

與上面那個方法類似,只不過Java的類可以實現多個介面,所以返回的Type必須用陣列來儲存。

以上兩個方法返回的都是Type物件或陣列,在我們的這個話題中,Class都是代表的引數化型別,因此可以將Type物件Cast成ParameterizedType物件。而 ParameterizedType物件有一個方法, getActualTypeArguments()。

public Type[] getActualTypeArguments()

用來返回一個Type物件陣列,這個陣列代表著這個Type宣告中實際使用的型別。接著使用上面的例子:

import java.lang.reflect.ParameterizedType; 
public class GT1 extends GT2<Integer>{ 
  public static void main(String[] args) { 
  System.out.println(((ParameterizedType)new GT1().getClass().getGenericSuperclass()).getActualTypeArguments()[0]); 
  } 
}

這次的顯示結果將是:class java.lang.Integer

因此,我們可以通過繼承+反射的方法,來的到T.class。

需要說明的是,江南白衣使用的方法是將關鍵語句

 Class < T > entityClass = (Class < T > ) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[ 0 ];

放在了超類,也就是宣告泛型的那個類的構造方法中。這樣一來,子類在繼承具有泛型的超類時,會自動呼叫超類的構造方法。在此超類的構造方法中,呼叫的getClass返回的是子類的Class型別(與通常的重寫機制有悖,呵呵,有待深究,但測試結果確是如此),則在子類中就無需再顯式地使用getGenericInterfaces()和getGenericSuperclass()等方法了。

接著,再使用(Class<T>)對 getActualTypeArguments()返回的元素做casting,即可得到所謂的T.class。

泛型之後,所有BaseHibernateEntityDao的子類只要定義了泛型,就無需再過載getEnttityClass(),get()函式和find()函式,銷益挺明顯的,所以SpringSide的Dao基類毫不猶豫就泛型了。

不過擦拭法的大棒仍在,所以子類的泛型語法可不能亂寫,最正確的用法只有:

public class BookDao extends BaseHibernateEntityDao<Book>

個人見解:即他們之間的關係就是 BaseHibernateEntityDao 是一個父類,他可以是單純的一個類,也可以是實現介面的類,而應其中泛型的緣故,所以他直接例項化呼叫其中方法是沒有意義的,因為泛型的型別無法確定,所以只有通過用其它類去繼承它,在繼承時將泛型傳入,此時若例項化繼承了BaseHibernateEntityDao 的類,BaseHibernate EntityDao的構造器中就會通過反射確定該泛型的型別.(有誤請指正)

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。