轉載請註明出處:https://my.oschina.net/u/874727/blog/747427java
Q:1025250620數組
在不少Java的開源項目中都用到Java的泛型。好比Gson,就能夠經過TypeToken<T>裏的泛型參數來指定生成的類型。鑑於網上關於泛型的文章並很少,爲了非墨後面項目研究的須要,非墨開始研究這部分的API。首先咱們先來看一下在Java語言中泛型的例子:bash
public class MyTest<T1,T2 extends Number> { T1 member; public <T> void method(T m) {} }
上述代碼中的標誌:T1,T2,T都是泛型類型。Java的泛型檢查發生在編譯期,可是會在編譯後的JVM字節碼中增長類型判斷的語句。爲了方便你們理解這句話咱們用一段代碼測試一下:測試
List<String> list = new ArrayList<>(); try { Method m = list.getClass().getDeclaredMethod("add", new Class[]{Object.class}); m.invoke(list, 1); m.invoke(list, 2); } catch (Exception e) { System.out.println("error"); }
在上述代碼中,雖然List變量指定了內部元素的類型String,可是在JVM運行期間list對象的add方法調用的仍是add(Ljava/lang/Object)簽名的方法。因此此段代碼執行之後,控制檯不會有任何的輸出。基於此段代碼的基礎上,咱們增長另外一段驗證代碼:.net
for (Object o:list) { System.out.println(">>"+o); }//println >>1 >>2 //will error System.out.println("--->"+list.get(0));
咱們打印list中的子元素,若是子元素類型是Object類型的話代碼正常運行。可是若是咱們直接經過list.get的方式來獲取元素的時候程序就會拋出一個Class Cast異常。這是爲何呢?咱們來看一下編譯後的JVM字節碼:scala
95: iconst_0 96: invokeinterface #56, 2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 101: checkcast #62; //class java/lang/String
咱們看到,Java編譯器在執行段後插入了"checkcast"指令。也就是說Java的泛型只不過是在執行期間增長了一些類型的檢查語句。可是,儘管泛型發生在編譯器,可是java仍是把類型記錄在類型對象中。這就是咱們今天討論的主角Type對象。咱們看一下Type類型的繼承樹:code
能夠看出,Type類型有四個直接接口子類,一個實現類。此外還有另一個接口GenericDeclaration。這個接口註明哪一個類能夠聲明泛型。按照咱們經過第一代碼塊能夠知道,在Java語言中,能夠聲明泛型的是類,方法。爲何沒有成員變量呢?咱們在第一個代碼塊中的"T1 member"不也是泛型聲明麼?對象
或許這裏咱們應該換一個說法,對於member變量來講,它只是使用了泛型而並非聲明瞭泛型,聲明T1泛型的是MyTest類。咱們把構造器也當作方法的一部分,方法做爲可執行提,在方法和構造器類的基礎上JAVA又作了一層抽象--Executable類。blog
Type類型的四個直接子類註釋裏已經給出瞭解釋。因爲這些東西在javadoc中也說的並不詳細,也很難用辭藻把他描述的很是清楚。爲了讓你們理解,非墨用一些簡單的代碼來讓你們更加直觀的理解他們是個什麼東東。咱們先將可能涉及到的泛型類型和聲明方式都寫到一個類中:繼承
public static class TypeClazz<T1, T2 extends Number> { public T2 member; public T1 member2; public Collection<? extends Number> collection; public Collection<T2> collection2; public T2[] array; public <T extends Type> void method(T p1,T2 p2) {} }
這個類基本涵蓋了全部的泛型狀況。咱們還須要增長一些方法來打印咱們所關心的信息:
public static Type printlnFieldType(String name) { System.out.println("name:"+name); Class clazzType = TypeClazz.class; Type type = null; try { Field field = clazzType.getDeclaredField(name); type = field.getGenericType(); printlnType(field.getGenericType()); } catch (Exception e) {} if (type instanceof ParameterizedType) { ParameterizedType ptype = (ParameterizedType)type; Type[] types = ptype.getActualTypeArguments(); for (Type t:types) { System.out.print(">>"); printlnType(t); } } return type; } public static void printlnMethodReturnType(String name) { System.out.println("name:"+name); Class clazzType = TypeClazz.class; try { Method[] ms = clazzType.getDeclaredMethods(); Method method = null; for (Method m:ms) { if(m.getName().equals(name)) { method = m; break; } } printlnType(method.getGenericReturnType()); } catch (Exception e) { e.printStackTrace(); } } public static void printlnMethodParamTypes(String name) { System.out.println("name:"+name); Class clazzType = TypeClazz.class; try { Method[] ms = clazzType.getDeclaredMethods(); Method method = null; for (Method m:ms) { if(m.getName().equals(name)) { method = m; break; } } Type[] types = method.getGenericParameterTypes(); for (Type t:types) { printlnType(t); } } catch (Exception e) { e.printStackTrace(); } }
對於屬性,咱們只關心它的類型,而對於方法,咱們不只須要關心它的參數類型,還要關心它的返回類型。咱們來調用一下以上的信息:
public static void main(String ...args) { printlnFieldType("member"); printlnFieldType("member2"); printlnFieldType("collection"); printlnFieldType("collection2"); printlnFieldType("array"); printlnMethodReturnType("method"); printlnMethodParamTypes("method"); }
控制檯輸出:
name:member >>>TypeVariable name:member2 >>>TypeVariable name:collection >>>ParameterizedType >>>>>WildcardType name:collection2 >>>ParameterizedType >>>>>TypeVariable name:array >>>GenericArrayType name:method >>>class name:method >>>TypeVariable >>>TypeVariable
對於直接採用泛型方式定義的member來講,它都是TypeVariable類型。而對於包含有泛型定義的collection來講,它屬於參數化的ParameterizedType類型。因爲數組類型是單獨的類型,而且由虛擬機動態生成,所以,Type子類中有專門針對數組的GenericArrayType類型。例子中的array成員就是這種類型。咱們揭開了collection的面紗後,泛型定義爲"<? extends Number>"。這個就是通配符泛型WildcardType。
相信以上的例子已經很能解釋這四種類型的定義了,下一篇,非墨將帶着這些基礎知識,深刻到一個開源項目的源碼中,看下別人的項目是如何巧妙的運用這點的。