正文前先來一波福利推薦:html
福利一:java
百萬年薪架構師視頻,該視頻能夠學到不少東西,是本人花錢買的VIP課程,學習消化了一年,爲了支持一下女友公衆號也方便你們學習,共享給你們。sql
福利二:數據庫
畢業答辯以及工做上各類答辯,平時積累了很多精品PPT,如今共享給你們,大大小小加起來有幾千套,總有適合你的一款,不少是網上是下載不到。數組
獲取方式:微信
微信關注 精品3分鐘 ,id爲 jingpin3mins,關注後回覆 百萬年薪架構師 ,精品收藏PPT 獲取雲盤連接,謝謝你們支持!架構
-----------------------正文開始---------------------------框架
一、背景介紹
在實現SSH框架中,DAO層向數據庫持久化的過程當中,由於大部分保存對象的方法都會調用到sava();全部索性就把save delete update select 方法進行封裝到父類中,這時候就遇到了個問題,子類在調用這些方法的時候,須要根據子類的類型獲知子類Class類型;這個時候能夠經過傳入泛型,根據泛型的類型來獲取子類的Class類型; 函數
二、實現代碼範例
父類:public abstract class Parents<E>學習
{
private Class<?> child;
public Parents() { Class<?> c = this.getClass(); //子類建立 會建立父類 子類調用時 此處的this是子類 Type t = c.getGenericSuperclass(); //得到帶有泛型的父類 if (t instanceof ParameterizedType) { Type[] p = ((ParameterizedType) t).getActualTypeArguments(); //取得全部泛型 this.child= (Class<E>) p[0]; }
}
在子類調用父類的方法時,若是父類的方法中須要知道具體子類的Class類型 則能夠直接使用Child來使用;
此處的原理就是在子類繼承父類的時候 帶有泛型 而後子類在建立的時候,會調用父類的構造函數,構造函數中存在this指的的是子類,而後經過得到父類,再得到父類的泛型
;經過泛型找到子類類型;
三、原理分析
該實現是經過反射技術實現;下面看具體的分析;
三、1 ParameterizedType 類
ParameterizedType,參數化類型,形如:Object<T, K>,即常說的泛型,是Type的子接口。
public interface ParameterizedType extends Type { //1.得到<>中實際類型 Type[] getActualTypeArguments(); //2.得到<>前面實際類型 Type getRawType(); //3.若是這個類型是某個類型所屬,得到這個全部者類型,不然返回null Type getOwnerType(); }
1.getActualTypeArguments
得到參數化類型中<>裏的類型參數的類型,由於可能有多個類型參數,例如Map<K, V>,因此返回的是一個Type[]數組。
注意:不管<>中有幾層<>嵌套,這個方法僅僅脫去最外層的<>,以後剩下的內容就做爲這個方法的返回值,因此其返回值類型不必定。
例如:
1. List<ArrayList> a1;//這裏返回的是,ArrayList,Class類型 2. List<ArrayList<String>> a2;//這裏返回的是ArrayList<String>,ParameterizedType類型 能夠繼續經過調用getActualTypeArguments得到其泛型類型 3. List<T> a3;//返回的是T,TypeVariable類型 4. List<? extends Number> a4; //返回的是WildcardType類型 5. List<ArrayList<String>[]> a5;//GenericArrayType 要注意,ArrayList與ArrayList<String>的不一樣。
public static void main(String[] args) throws Exception { Method method = new Main().getClass().getMethod("test", List.class);//這裏的第二個參數,和getRawType()意義相似 Type[] types = method.getGenericParameterTypes(); ParameterizedType pType = (ParameterizedType) types[0]; Type type = pType.getActualTypeArguments()[0]; System.out.println(type); //type是Type類型,但直接輸出的不是具體Type的五種子類型,而是這五種子類型以及WildcardType具體表現形式 System.out.println(type.getClass().getName()); } public void test(List<ArrayList<String>[]> a)
{ }
2.getRawType
返回最外層<>前面那個類型,即Map<K ,V>的Map
Map<Integer, String> maps = new HashMap<>(); ParameterizedType pType = (ParameterizedType) maps.getClass().getGenericSuperclass();//得到HashMap的父類 System.out.println(pType.getRawType());//class java.util.AbstractMap if(pType.getRawType() instanceof Class){ System.out.println("true");//true } //注意類型(Type)與類(Class)的區別
三、2 Type類
Type是java類型信息體系中的頂級接口,其中Class就是Type的一個直接實現類。此外,Type還有有四個直接子接口:ParameterizedType,TypeVariable,WildcardType,GenericArrayType。
引用這位仁兄對這幾個接口的介紹[轉載]:
Type
它是全部類型的公共接口。包括原始類型、參數化類型、數組類型、類型變量和基本類型。ParameterizedType, TypeVariable, WildcardType,GenericArrayType這四個接口都是它的子接口。
三、二、1 GenericDeclaration
這個接口Class、Method、Constructor都有實現,咱們就是要用這個接口的getTypeParameters方法,它返回一個TypeVariable[]數組,這個數組裏面就是咱們定義的類型變量T和K,順序與咱們聲明時同樣。若是用循環語句將數組打印出來,你會發現只會輸出T和K,這可不是咱們想要的結果,那麼想要得到預期的結果怎麼辦呢?請繼續往下看。
三、二、2 TypeVariable
它表示類型變量。好比T,好比K extends Comparable<? super T> & Serializable,這個接口裏面有個getBounds()方法,它用來得到類型變量上限的Type數組,若是沒有定義上限,則默認設定上限爲Object,請注意TypeVariable是接口,實際獲得的是TypeVariableImpl實現類,下面幾個接口都同樣。
拿T和K來講明,T沒有定義任何上限,因此它就有一個默認上限java.lang.Object,實際跟蹤代碼的時候你會發現T的bounds屬性爲空,只有在調用了getBounds()方法後,纔會有一個Type[1]數組[class java.lang.Object]。而對於K來講,調用了getBounds方法後,獲得的數組是[java.lang.Comparable<? super T>, interface java.io.Serializable],它們的類型倒是不同的,第1個是ParameterizedType,而第二個是Class
三、二、3 ParameterizedType
ParameterizedType表示參數化類型,就是上面說的java.lang.Comparable<? super T>,再好比List<T>,List<String>,這些都叫參數化類型。獲得Comparable<? super T>以後,再調用getRawType()與getActualTypeArguments()兩個方法,就能夠獲得聲明此參數化類型的類(java.lang.Comparable)和實際的類型參數數組([? super T]),而這個? super T又是一個WildcardType類型。
三、二、4 WildcardType
它用來描述通配符表達式,上面返回的? super T正好是這個類型。而後調用getUpperBounds()上限和getLowerBounds()下限這兩個方法,得到類型變量?的限定類型(上下限),對於本例的通配符(?),它的上限爲java.lang.Object,下限爲T
經過上面幾個接口的分析,能夠將Person類的泛型參數都解析出來,那麼Person的超類以及實現的接口該怎麼處理呢?Class類裏面一樣在1.5版本加入了getGenericSuperclass()與getGenericInterfaces()兩個方法,用於返回帶參數化類型的超類與接口。
三、二、5 GenericArrayType其實就是泛型數組類型。
咱們說Class在必定程度上挽救了擦除的類型信息,咱們就能夠經過這幾個接口來獲取被擦除的類型參數信息,這幾個接口無非就是對類型參數的一個分類,經過它們提供的一些方法,咱們能夠逐步的獲取到最原始的類型參數的Class對象。
具體的說明和API你們能夠去看文檔,我這裏記錄一個實際的應用,固然在各類框架中的應用比比皆是。
在JavaEE的Dao層咱們通常都會封裝出一個通用的泛型BaseDao,它能夠實現對各類實體例如User,Order的基本CRUD,而後具體的UserDao,OrderDao等等會去繼承它,提供其餘的Dao方法:
public class UserDao extends BaseDao<User>{} 我使用的BaseDao是基於DBUtils的,它須要實體的Class對象才能進行通用的查詢方法,例如User的Class對象,咱們能夠經過構造函數,函數參數等手段傳遞給BaseDao,可是有了反射,能夠有更優雅的實現。 public class BaseDao<T> { private Class<T> clszz; public BaseDao(){ Type type = this.getClass().getGenericSuperclass();//拿到帶類型參數的泛型父類 if(type instanceof ParameterizedType){//這個Type對象根據泛型聲明,就有多是4中接口之一,若是它是BaseDao<User>這種形式 ParameterizedType parameterizedType = (ParameterizedType) type; Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();//獲取泛型的類型參數數組 if(actualTypeArguments != null && actualTypeArguments.length == 1){ if(actualTypeArguments[0] instanceof Class){//類型參數也有可能不是Class類型 this.clszz = (Class<T>) actualTypeArguments[0]; }else{ //例如: BaseDao<BaseDao<User>>,獲取到的就不是Class,而又是ParameterizedType,即嵌套的 ParameterizedType,一層一層剝開,最終是能夠獲得User的Class對象的 } } } } public T get(String sql,Object...params){ QueryRunner qr = new QueryRunner(); T obj; Connection connection; try { connection = JdbcUtil.getConnection(); obj = qr.query(connection,sql,new BeanHandler<T>(clszz),params); } catch (SQLException e) { e.printStackTrace(); return null; } return obj; } }